package com.niiwoo.civet.trade.service.local.project;

import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.dubbo.rpc.RpcContext;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dangdang.ddframe.rdb.sharding.api.HintManager;
import com.dangdang.ddframe.rdb.sharding.hint.HintManagerHolder;
import com.niiwoo.activity.event.enums.UsableCreditChangeTypeEventEnum;
import com.niiwoo.asset.cardniu.dto.mq.CardNiuExternalMQConstants;
import com.niiwoo.asset.cardniu.dto.request.CardniuUserDeviceInfoDTO;
import com.niiwoo.asset.cardniu.dto.request.LoanStatusDTO;
import com.niiwoo.asset.cardniu.service.loan.LoanDubboService;
import com.niiwoo.asset.constant.MqConstants;
import com.niiwoo.asset.dto.mq.R360OrderStatusChangedDTO;
import com.niiwoo.asset.enums.r360.R360OrderStatusEnum;
import com.niiwoo.asset.org.business.constants.OrgRabbitConstant;
import com.niiwoo.asset.org.business.dto.mq.ProjectStatusChangeDTO;
import com.niiwoo.civet.account.dto.response.AccountBaseResponse;
import com.niiwoo.civet.account.service.AccountDubboService;
import com.niiwoo.civet.account.service.AccountWithdrawDubboService;
import com.niiwoo.civet.base.constant.OrgConstant;
import com.niiwoo.civet.base.dto.BaseFeeDetailDTO;
import com.niiwoo.civet.base.enums.*;
import com.niiwoo.civet.trade.constant.MqConstant;
import com.niiwoo.civet.trade.constant.TradeRedisKey;
import com.niiwoo.civet.trade.dao.entity.*;
import com.niiwoo.civet.trade.dao.mapper.*;
import com.niiwoo.civet.trade.dto.ProjectBorrowerPlanNewDTO;
import com.niiwoo.civet.trade.dto.ProjectNotifyDTO;
import com.niiwoo.civet.trade.dto.SummaryStatusDTO;
import com.niiwoo.civet.trade.dto.Tuple2;
import com.niiwoo.civet.trade.dto.common.ProjectAndBorrowerPlanDTO;
import com.niiwoo.civet.trade.dto.common.ProjectBorrowerPlanDTO;
import com.niiwoo.civet.trade.dto.common.ProjectDTO;
import com.niiwoo.civet.trade.dto.common.ProjectDynamicDTO;
import com.niiwoo.civet.trade.dto.newRefund.RefundStateDTO;
import com.niiwoo.civet.trade.dto.request.*;
import com.niiwoo.civet.trade.dto.request.databackflow.AbortiveProjectBackFlowRequestDTO;
import com.niiwoo.civet.trade.dto.request.databackflow.ProjectStatusChangeBackFlowRequestDTO;
import com.niiwoo.civet.trade.dto.request.loan.ProjectWaitPublishReqDTO;
import com.niiwoo.civet.trade.dto.request.loan.PushProjectRequestDTO;
import com.niiwoo.civet.trade.dto.response.*;
import com.niiwoo.civet.trade.dto.response.newRefund.RefundFreezeParamResponseDTO;
import com.niiwoo.civet.trade.dto.response.org.AssetOrgProjectResponseDTO;
import com.niiwoo.civet.trade.enums.*;
import com.niiwoo.civet.trade.enums.Intelligence.ProjectPackageStatusEnum;
import com.niiwoo.civet.trade.service.dubbo.loan.core.ISpeedLoanFactory;
import com.niiwoo.civet.trade.service.dubbo.loan.core.ISpeedLoanService;
import com.niiwoo.civet.trade.service.local.activity.ActivityEventService;
import com.niiwoo.civet.trade.service.local.common.ProjectCommonService;
import com.niiwoo.civet.trade.service.local.configure.TradeConfigLocalService;
import com.niiwoo.civet.trade.service.local.databackflow.TianChengDataBackFlowLocalService;
import com.niiwoo.civet.trade.service.local.flowexecute.ProjectFlowExecuteService;
import com.niiwoo.civet.trade.service.local.fund.FundRepaymentService;
import com.niiwoo.civet.trade.service.local.intelligenceInvest.IntelligenceInvestService;
import com.niiwoo.civet.trade.service.local.intelligenceInvest.ProjectPackageLocalService;
import com.niiwoo.civet.trade.service.local.invest.InvestService;
import com.niiwoo.civet.trade.service.local.invest.ProjectInvestPrizeService;
import com.niiwoo.civet.trade.service.local.loan.SpeedLoanDispenseChannelService;
import com.niiwoo.civet.trade.service.local.loan.SpeedLoanService;
import com.niiwoo.civet.trade.service.local.newRefund.common.RefundCommonService;
import com.niiwoo.civet.trade.service.local.newRefund.refundDeal.converter.RefundEnumConverter;
import com.niiwoo.civet.trade.service.local.newRefund.refundDeal.core.FeeCalcForRefundService;
import com.niiwoo.civet.trade.service.local.notify.Notifiers;
import com.niiwoo.civet.trade.service.local.org.OrgProjectLocalService;
import com.niiwoo.civet.trade.service.local.refund.CommonLocalService;
import com.niiwoo.civet.trade.service.local.third.NotifyAssetLocalService;
import com.niiwoo.civet.trade.utils.RepaymentUtil;
import com.niiwoo.civet.user.dto.response.NiiwooScoreResponseDTO;
import com.niiwoo.civet.user.dto.response.UserAccountPropertyResponse;
import com.niiwoo.civet.user.dto.response.organization.OrganizationBaseConfigRespDTO;
import com.niiwoo.civet.user.service.OrganizationDubboService;
import com.niiwoo.civet.user.service.UserAccountDubboService;
import com.niiwoo.civet.user.service.UserBasicInfoDubboService;
import com.niiwoo.tripod.base.utils.DateUtils;
import com.niiwoo.tripod.lanmao.enums.UserRoleEnum;
import com.niiwoo.tripod.provider.exception.BizException;
import com.niiwoo.tripod.service.component.SnowflakeIdWorker;
import com.niiwoo.tripod.tiancheng.component.TianchengDataFlowService;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationAdapter;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.Assert;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.nio.charset.Charset;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
public class ProjectLocalService {
    @Autowired
    private ProjectMapperExt projectMapperExt;

    @Autowired
    private ProjectLendRecordMapperExt projectLendRecordMapperExt;

    @Autowired
    private ProjectBorrowerPlanNewMapperExt projectBorrowerPlanNewMapperExt;

    @Autowired
    private ProjectBorrowerPlanDetailMapperExt projectBorrowerPlanDetailMapperExt;

    @Autowired
    private ProjectAuditDecisionMapperExt projectAuditDecisionMapperExt;
    @Autowired
    private RateConfigMapperExt rateConfigMapperExt;
 

    @Autowired
    private AuditSourceMapperExt auditSourceMapperExt;

    @Reference(version = "1.0.0")
    private AccountWithdrawDubboService accountWithdrawDubboService;
    @Reference(version = "1.0.0")
    private LoanDubboService loanDubboService;
    @Reference(version = "1.0.0")
    UserBasicInfoDubboService userBasicInfoDubboService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    ProjectBackoutBorrowPrizeMapperExt projectBackoutBorrowPrizeMapperExt;
    @Autowired
    private ProjectDynamicMapperExt projectDynamicMapperExt;
    @Autowired
    private SnowflakeIdWorker snowflakeIdWorker;
    @Autowired
    private CommonLocalService commonLocalService;
    @Autowired
    InvestService investService;
    @Autowired
    private ProjectBorrowerSummaryMapperExt projectBorrowerSummaryMapperExt;
    @Autowired
    private FundRepaymentService fundRepaymentService;
    @Autowired
    private TradeConfigLocalService tradeConfigLocalService;
    @Autowired
    private SpeedLoanService speedLoanService;
    @Autowired
    private ProjectPackageDetailMapperExt projectPackageDetailMapperExt;
    @Autowired
    private ProjectPackageMapperExt projectPackageMapperExt;
    
    @Autowired
    Notifiers notifiers;

    @Autowired
    ProjectFlowService projectFlowService;
    @Autowired
    TianchengDataFlowService tianchengDataFlowService;
    @Autowired
    TianChengDataBackFlowLocalService tianChengDataBackFlowLocalService;

    @Autowired
    private ActivityEventService activityEventService;
    @Autowired
    private NotifyAssetLocalService notifyAssetLocalService;
    @Autowired
    private AssetOrgProjectMapperExt assetOrgProjectMapperExt;
    @Autowired
    private ISpeedLoanFactory speedLoanFactory;
    @Autowired
    private ProjectCommonService projectCommonService;
    @Autowired
    OrgProjectLocalService orgProjectLocalService;
    @Autowired
    private RefundOrderMapperExt refundOrderMapperExt;

    @Autowired
    private RefundCommonService refundCommonService;

    @Autowired
    private FeeCalcForRefundService feeCalcForRefundService;

	@Reference(version="1.0.0")
	private AccountDubboService accountDubboService;

    @Reference(version="1.0.0")
    private UserAccountDubboService userAccountDubboService;

    @Autowired
    ProjectFlowExecuteService projectFlowExecuteService;

    @Autowired
    ProjectFlowExecuteMapperExt projectFlowExecuteMapperExt;

    @Autowired
    ProjectFlowExecuteRuntimeMapperExt projectFlowExecuteRuntimeMapperExt;
    @Autowired
    private ProjectRateMapper projectRateMapper;

    @Autowired
    private AdvanceConfigMapperExt advanceConfigMapperExt;

    @Autowired
    private ProjectWaitPublishMapperExt projectWaitPublishMapperExt;

    @Autowired
    private ProjectInvestingMapperExt projectInvestingMapperExt;

    @Autowired
    private ProjectInvestOrderMapperExt projectInvestOrderMapperExt;

    @Autowired
    private ProjectDeliveryChannelMapperExt projectDeliveryChannelMapperExt;

    static final int PRECISION_TWO = 2;

    @Autowired
    private ProjectInvestPrizeService projectInvestPrizeService;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Autowired
    private SpeedLoanDispenseChannelService speedLoanDispenseChannelService;

    @Autowired
    private TradeConfigLocalService tradeConfigService;
    
    @Autowired
    private IntelligenceInvestService intelligenceInvestService;

    @Reference(version = "1.0.0")
    private OrganizationDubboService organizationDubboService;

    public ProjectDTO selectProjectById(Long projectId) {
        if (!HintManagerHolder.isMasterRouteOnly()) {
            HintManagerHolder.clear();
            HintManager hintManager = HintManager.getInstance();
            hintManager.setMasterRouteOnly();
        }
        return JSON.parseObject(JSON.toJSONString(projectMapperExt.selectByPrimaryKey(projectId)), ProjectDTO.class);
    }

    public Byte selectProjectLendRecordByProjectId(Long projectId) {
        ProjectLendRecord record = projectLendRecordMapperExt.queryProjectLendRecordByProjectId(projectId);
        Assert.notNull(record, "标的放款记录不存在");
        Assert.notNull(record.getWithdrawOrderId(), "提现订单ID不存在");
        return accountWithdrawDubboService.selectWithdrawOrderStatusById(record.getWithdrawOrderId());
    }

    public ProjectAndBorrowerPlanDTO selectProjectAndPlanByProjectIdAndPeriod(Long projectId, Integer period) {
        // 查询标的信息
        Project project = Optional.ofNullable(projectMapperExt.selectByPrimaryKey(projectId))
                .orElseThrow(() -> new BizException("TRD20001"));
        ProjectDTO projectDTO = JSON.parseObject(JSON.toJSONString(project), ProjectDTO.class);

        // 还款计划信息
        ProjectBorrowerPlanNewDTO projectBorrowerPlanNewDTO = Optional.ofNullable(projectBorrowerPlanNewMapperExt.selectByProjectIdAndPeroidForAmount(projectId, period))
                .orElseThrow(() -> new BizException("TRD20009"));

        // 转化对象
        ProjectBorrowerPlanDTO projectBorrowerPlanDTO = new ProjectBorrowerPlanDTO();

        projectBorrowerPlanDTO.setId(projectBorrowerPlanNewDTO.getId());
        projectBorrowerPlanDTO.setProjectId(projectBorrowerPlanNewDTO.getProjectId());
        projectBorrowerPlanDTO.setBorrowerUserId(projectBorrowerPlanNewDTO.getUserId());
        projectBorrowerPlanDTO.setCurrentPeriod(projectBorrowerPlanNewDTO.getPeriod());
        projectBorrowerPlanDTO.setTotalPeriod(projectBorrowerPlanNewDTO.getTotalPeriod());
        projectBorrowerPlanDTO.setRefundDate(projectBorrowerPlanNewDTO.getRefundDate());
        projectBorrowerPlanDTO.setOverTime(projectBorrowerPlanNewDTO.getOverDate());
        projectBorrowerPlanDTO.setRefundStatus(projectBorrowerPlanNewDTO.getStatus());
        // 还款年月
        if (null != projectBorrowerPlanNewDTO.getRefundDate()) {
            projectBorrowerPlanDTO.setYearMouth(new DateTime(projectBorrowerPlanNewDTO.getRefundDate()).toString("yyyyMM"));
        }

        projectBorrowerPlanDTO.setCapital(projectBorrowerPlanNewDTO.getCapital());
        projectBorrowerPlanDTO.setInterest(projectBorrowerPlanNewDTO.getInterest());
        projectBorrowerPlanDTO.setPenaltyFee(projectBorrowerPlanNewDTO.getPenaltyFee());
        projectBorrowerPlanDTO.setManagementFee(projectBorrowerPlanNewDTO.getManagementFee());

        projectBorrowerPlanDTO.setRefundedCapital(projectBorrowerPlanNewDTO.getRefundedCapital());
        projectBorrowerPlanDTO.setRefundedInterest(projectBorrowerPlanNewDTO.getRefundedInterest());
        projectBorrowerPlanDTO.setRefundedPenaltyFee(projectBorrowerPlanNewDTO.getRefundedPenaltyFee());
        projectBorrowerPlanDTO.setRefundedManagementFee(projectBorrowerPlanNewDTO.getRefundedManagementFee());

        projectBorrowerPlanDTO.setCreateTime(projectBorrowerPlanNewDTO.getCreateTime());
        projectBorrowerPlanDTO.setUpdateTime(projectBorrowerPlanNewDTO.getUpdateTime());

        return new ProjectAndBorrowerPlanDTO(projectDTO, projectBorrowerPlanDTO);
    }

    /**
     * 查询极速借记录统计信息
     *
     * @param userId
     * @return
     */
    public SpeedLoanRecordStatisticsDTO querySpeedLoanRecordStatistics(String userId) {
        log.info("查询极速借记录统计信息:{}", userId);
        SpeedLoanRecordStatisticsDTO speedLoanRecordStatisticsDTO = new SpeedLoanRecordStatisticsDTO();
        try {
            List<Integer> subStatus = new ArrayList<>();
            // 3还款中
            subStatus.add(ProjectSubStatusEnum.REPAYING_INIT.getSubStatus()); // 还款中-正常还款中
            subStatus.add(ProjectSubStatusEnum.REPAYING_OVERDUE.getSubStatus()); // 还款中-逾期还款中
            subStatus.add(ProjectSubStatusEnum.REPAYING_BAD.getSubStatus()); // 还款中-坏账还款中

            // 4已结清
            subStatus.add(ProjectSubStatusEnum.FINISH_NORMAL.getSubStatus());
            subStatus.add(ProjectSubStatusEnum.FINISH_ADVANCE.getSubStatus());
            subStatus.add(ProjectSubStatusEnum.FINISH_BAD.getSubStatus());
            subStatus.add(ProjectSubStatusEnum.FINISH_OVERDUE.getSubStatus());

            Integer settlementNum = 0; // 已结清记录数
            Integer inRepaymentNum = 0; // 正常还款中的记录数
            Integer inOverdueNum = 0; // 逾期还款中的记录数
            BigDecimal totalLoanAmount = new BigDecimal(0); // 累计借款金额
            BigDecimal settlementAmount = refundCommonService.selectRefundCapitalByUser(userId); // 已还总金额
            BigDecimal inRepaymentAmount = refundCommonService.selectRepaymentAmountByUser(userId); // 正常还款中的总金额
            BigDecimal inOverdueAmount = refundCommonService.selectOverdueAmountByUser(userId);// 逾期还款中的总金额
            // 用户历史逾期期数
            Integer overdueCount = projectBorrowerPlanNewMapperExt.countByUserIdForOverduePeriod(userId, commonLocalService.getCurrentDate());
            settlementAmount = settlementAmount == null ? BigDecimal.ZERO : settlementAmount;
            inRepaymentAmount = inRepaymentAmount == null ? BigDecimal.ZERO : inRepaymentAmount;
            inOverdueAmount = inOverdueAmount == null ? BigDecimal.ZERO : inOverdueAmount;

            List<Project> projectList = projectMapperExt.queryAllProjectByUserId(userId, subStatus);
            for(Project project : projectList){
                totalLoanAmount = totalLoanAmount.add(project.getContractAmount());
                if (project.getStatus().byteValue() == ProjectStatusEnum.FINISH.getStatus()) {
                    ++settlementNum;
                } else {
                    // 查询待还款金额
                    if (project.getSubStatus() == ProjectSubStatusEnum.REPAYING_INIT.getSubStatus()) {
                        ++inRepaymentNum;
                    } else {
                        ++inOverdueNum;
                    }
                }
            }
            speedLoanRecordStatisticsDTO.setTotalOverDueNum(overdueCount);
            speedLoanRecordStatisticsDTO.setSettlementNum(settlementNum);
            speedLoanRecordStatisticsDTO.setSettlementAmount(settlementAmount);
            speedLoanRecordStatisticsDTO.setInRepaymentNum(inRepaymentNum);
            speedLoanRecordStatisticsDTO.setInRepaymentAmount(inRepaymentAmount);
            speedLoanRecordStatisticsDTO.setInOverdueNum(inOverdueNum);
            speedLoanRecordStatisticsDTO.setInOverdueAmount(inOverdueAmount);
            speedLoanRecordStatisticsDTO.setSuccessProjectNum(projectList.size());
            speedLoanRecordStatisticsDTO.setTotalLoanAmount(totalLoanAmount);
        } catch (Exception e) {
            log.error("查询极速借记录统计信息，异常！userId:{}", userId, e);
        }
        return speedLoanRecordStatisticsDTO;
    }

    public void sendMqToAssetSide(String userId) {
        // 根据userId查询标的表,按照创建时间倒序,标的来源为360,取第一个
        Project project = projectMapperExt.selectLastR360ByUserId(userId);
        if (project == null) {
            return;
        }
        Map<String, Long> map = new HashMap<>();
        map.put("projectId", project.getProjectId());
        Message msg = new Message(JSON.toJSONString(map).getBytes(Charset.forName("UTF-8")), null);
        rabbitTemplate.send(MqConstants.Exchange.ASSET_EXCHANGE, MqConstants.getRoutingKey(MqConstants.Exchange.ASSET_EXCHANGE, MqConstants.Queue.REPAYMENT_PLAN_PUSH), msg);
    }

    /**
     * 流标, 修改标的状态和创建借款红包退回记录
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public Tuple2<Boolean, ProjectBackoutBorrowPrize> flow(ProjectFlowRequestDTO requestDTO) {
    	Project pInfo = projectMapperExt.selectByPrimaryKey(requestDTO.getProjectId());
    	//智能投专用
    	ProjectPackage projectPackage = null;
    	//如果是智能投流标，需要先锁住包，从大大小锁，防止死锁
    	if(isZhiNengTou(pInfo.getChannelCode())) {
    		Long packageId=	projectPackageDetailMapperExt.selectPackageIdByProjectId(pInfo.getProjectId());
    		projectPackage = projectPackageMapperExt.selectByPrimaryKeyForUpdate(packageId);
    		log.info("智能投触发流标事件，包ID{},标的id{}",packageId,pInfo.getProjectId());
    		//抢光，售罄的不能流标
    		if(ProjectPackageStatusEnum.PRESALE_OUT.getStatus().equals(projectPackage.getStatus())||ProjectPackageStatusEnum.SALE_OUT.getStatus().equals(projectPackage.getStatus())) {
    			return new Tuple2<>(false, null);
    		}
    	}
    	
        Project p = projectMapperExt.selectByPrimaryKeyForUpdate(requestDTO.getProjectId());
        if (p == null) {
            log.info("流标###标的不存在###projectId={}", requestDTO.getProjectId());
            return new Tuple2<>(false, null);
        }

        //审核通过已确认的标若取消借款，需要将标的从待发布池中移出;
        if(p.getSubStatus().equals(ProjectSubStatusEnum.CHECKING_FOR_CONFIRMED.getSubStatus())){
            projectWaitPublishMapperExt.deleteByPrimaryKey(p.getProjectId());
        }
        boolean collectingInit = p.getSubStatus().equals(ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus());
        boolean checking = p.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus());
        if (!(collectingInit || checking)) {
            log.info("流标###状态不合法###projectId={}, status={}, subStatus={}", p.getProjectId(), p.getStatus(), p.getSubStatus());
            return new Tuple2<>(false, null);
        }
        // 更新标的状态
        Project updateProject = new Project();
        updateProject.setProjectId(p.getProjectId());
        // updateProject.setBorrowUserId(p.getBorrowUserId());
        updateProject.setStatus(requestDTO.getSubStatus().getParentStatusEnum().getStatus());
        updateProject.setSubStatus(requestDTO.getSubStatus().getSubStatus());
        if (p.getAuditedTime() == null) {
            updateProject.setAuditedTime(new Date());
        }
        updateProject.setAuditManagerId(requestDTO.getManagerId());
        updateProject.setAuditManagerName(requestDTO.getManagerName());

        // 更新标的
        boolean isUpdateProjectSuccess = projectMapperExt.updateByPrimaryKeySelective(updateProject) > 0;
        if (!isUpdateProjectSuccess) {
            log.info("流标###标的更新状态失败###projectId={}, data={}", p.getProjectId(), JSON.toJSONString(updateProject));
            return new Tuple2<>(false, null);
        }
        //智能投逻辑处理
        if(Objects.nonNull(projectPackage)) {
        	BigDecimal amount =projectPackage.getAmount().subtract(p.getContractAmount());
        	projectPackage.setInvestedAmount(projectPackage.getInvestedAmount().subtract(p.getInvestedAmount()));
        	projectPackage.setAmount(amount);
        	projectPackage.setUpdateTime(new Date());
        	projectPackageMapperExt.updateByPrimaryKeySelective(projectPackage);
        }
        
        
        
        ProjectBackoutBorrowPrize backPrize = null;
        // 创建回退借款红包记录
        if (p.getPrizeId() != null) {
            backPrize = new ProjectBackoutBorrowPrize();
            backPrize.setProjectId(p.getProjectId());
            backPrize.setUserId(p.getBorrowUserId());
            backPrize.setPrizeId(p.getPrizeId());
            backPrize.setCreateTime(new Date());
            projectBackoutBorrowPrizeMapperExt.insertSelective(backPrize);
        }
        // 流标记录
        ProjectFlowed projectFlowed = new ProjectFlowed();
        projectFlowed.setProjectId(p.getProjectId());
        projectFlowed.setUserId(p.getBorrowUserId());
        ProjectSubStatusEnum subStatus = requestDTO.getSubStatus();
        String flowedReason;
        if (ProjectSubStatusEnum.MISCARRY_AUTO_FAILED.getSubStatus() == subStatus.getSubStatus()
                || ProjectSubStatusEnum.MISCARRY_LOAN_FAKE_FAILED.getSubStatus() == subStatus.getSubStatus()) {
            flowedReason = subStatus.getMsg();
        } else {
            flowedReason = subStatus.getDesc();
        }
        projectFlowed.setFlowedReason(flowedReason);
        projectFlowed.setManagerId(requestDTO.getManagerId());
        projectFlowed.setCheckedReason(requestDTO.getCheckedReason());
        projectFlowed.setRemark(requestDTO.getRemark());
        projectFlowed.setCreateTime(new Date());
        if (p.getSubStatus().equals(ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus())) {
            projectFlowed.setSyncStatus(((byte) 0)); // 需要同步存管状态
        } else {
            projectFlowed.setSyncStatus(((byte) 2)); // 无须同步存管状态
        }
        // 是否系统流标
        switch (requestDTO.getSubStatus()) {
            case MISCARRY_AUTO_FAILED:
            case MISCARRY_R360_TIMEOUT_UNCONFIRMED:
            case MISCARRY_LOAN_FAKE_FAILED: {
                projectFlowed.setIsSystemFlowed(true);
            }
            break;
            case MISCARRY_INVEST_OVERTIME:
            case MISCARRY_BACK_OVERTIME:
            case MISCARRY_MANUAL_FAILED:
            case MISCARRY_CANCER_BORROWER:
            case MISCARRY_CANCER_MANAGER:
            case MISCARRY_PUSH_FAIL:
            case MISCARRY_LOAN_BATCH: {
                projectFlowed.setIsSystemFlowed(false);
            }
            break;
        }
        projectFlowService.add(projectFlowed);

        //发送mq到活动标签系统（标签系统可用额度变更事件-流标）
        activityEventService.sendMsgForAviAmountChange(p.getBorrowUserId(), UsableCreditChangeTypeEventEnum.FLOW);
        // 撤投与投资红包
        investService.investCancelForProject(p.getProjectId(),requestDTO.getSubStatus().getSubStatus());

        // 5.1.5 流标之后关闭借款流程记录
        ProjectFlowExecute flowExecute = projectFlowExecuteMapperExt.selectLastByProjectIdForUpdate(p.getProjectId());
        if (flowExecute != null && flowExecute.getStatus() == (byte) 0) {
            ProjectFlowExecuteRuntime flowExecuteRuntime = projectFlowExecuteRuntimeMapperExt.selectByPrimaryKey(flowExecute.getFlowExecuteId());
            log.info("流标###关闭标的流程###, FlowExecuteId={}, status={}", flowExecute.getFlowExecuteId(), flowExecute.getStatus());
            projectFlowExecuteService.failEndProjectFlowExecute(flowExecute.getFlowExecuteId(), LoanNodeTypeEnum.enumof(flowExecuteRuntime.getNodeId()), String.format("流标###关闭标的流程###标的status=%s", flowExecute.getStatus()));
        }

        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
            @Override
            public void afterCommit() {
                super.afterCommit();
                try {
                	if(isZhiNengTou(pInfo.getChannelCode())) {
                		intelligenceInvestService.changProjectPackageStatusByProjectId(pInfo.getProjectId());
                	}
                    // 同步标的状态到存管
                    if (p.getSubStatus().equals(ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus())) {
                        projectFlowService.syncSingleFlowStatus(projectFlowed);
                    }
                    // 同步标的快照
                    if (requestDTO.getSubStatus() != ProjectSubStatusEnum.MISCARRY_INVEST_OVERTIME) {
                        JSONObject jsonObject = new JSONObject();
                        jsonObject.put("projectId", p.getProjectId());
                        jsonObject.put("borrowerUserId", p.getBorrowUserId());
                        jsonObject.put("productType", p.getType());
                        String json = JSON.toJSONString(jsonObject);
                        log.info("流标###发送标的快照mq###data={}", json);
                        rabbitTemplate.convertAndSend(MqConstant.CREATE_PROJECT_SNAPSHOT_EXCHANGE, MqConstant.CREATE_PROJECT_SNAPSHOT_ROUTE, json);
                    }
                    JSONObject auditWorkTaskObject = new JSONObject();
                    auditWorkTaskObject.put("projectId", p.getProjectId());
                    auditWorkTaskObject.put("auditManagerId", updateProject.getAuditManagerId());
                    auditWorkTaskObject.put("auditManagerName", updateProject.getAuditManagerName());
                    log.info("流标###发送auditWorkTask.mq###data={}", auditWorkTaskObject.toJSONString());
                    rabbitTemplate.convertAndSend(MqConstant.AUDIT_WORK_TASK_EXCHANGE, MqConstant.AUDIT_WORK_TASK_CANCEL_ROUTE, auditWorkTaskObject.toJSONString());

                    boolean isCardLoan = p.getType().equals(ProjectTypeEnum.CARD_NIU_LOAN.getValue());
                    boolean isFormCardNiu = p.getFromSource().equals(ProjectFromSourceEnum.CARD_NIU.getType());
                    if (isCardLoan && isFormCardNiu) {
                        /**
                         * 卡牛审核状态同步MQ
                         */
                        LoanStatusDTO loanLastStatusDTO = new LoanStatusDTO();
                        loanLastStatusDTO.setProjectId(p.getProjectId());
                        loanLastStatusDTO.setType(ProjectTypeEnum.CARD_NIU_LOAN.getValue());
                        loanLastStatusDTO.setStatus(requestDTO.getSubStatus().getParentStatusEnum().getStatus());
                        loanLastStatusDTO.setSubStatus(requestDTO.getSubStatus().getSubStatus());
                        rabbitTemplate.convertAndSend(CardNiuExternalMQConstants.Exchange.LOAN_STATUS_EXCHANGE,
                                CardNiuExternalMQConstants.getRoutingKey(CardNiuExternalMQConstants.Exchange.LOAN_STATUS_EXCHANGE, CardNiuExternalMQConstants.Queue.LOAN_STATUS_QUEUE),
                                loanLastStatusDTO);
                        switch (requestDTO.getSubStatus()) {
                            case MISCARRY_MANUAL_FAILED: {
                                ProjectNotifyDTO notifyDTO = new ProjectNotifyDTO();
                                BeanUtils.copyProperties(p, notifyDTO);
                                notifyDTO.setPushContent(requestDTO.getPushContent());
                                notifiers.notifyUser(NotifyTypeEnum.CARD_JS005, notifyDTO);
                            }
                            break;
                            case MISCARRY_INVEST_OVERTIME: {
                                // 借款人
                                notifiers.notifyUser(NotifyTypeEnum.CARD_JS011, p);
                                // 投资人
                                notifiers.notifyUser(NotifyTypeEnum.TS002, p);
                            }
                            break;
                            case MISCARRY_LOAN_BATCH:
                            case MISCARRY_CANCER_MANAGER: {
                                // 借款通知
                                ProjectNotifyDTO notifyDTO = new ProjectNotifyDTO();
                                BeanUtils.copyProperties(p, notifyDTO);
                                notifyDTO.setPushContent(requestDTO.getPushContent());
                                notifiers.notifyUser(NotifyTypeEnum.JS012, notifyDTO);
                                // 投资通知
                                notifiers.notifyUser(NotifyTypeEnum.TS003, p);
                            }
                            break;
                        }
                    } else {
                        switch (requestDTO.getSubStatus()) {
                            case MISCARRY_BACK_OVERTIME: {
                                notifiers.notifyUser(NotifyTypeEnum.JS024, p);
                            }
                            break;
                            case MISCARRY_MANUAL_FAILED: {
                                ProjectNotifyDTO notifyDTO = new ProjectNotifyDTO();
                                BeanUtils.copyProperties(p, notifyDTO);
                                notifyDTO.setPushContent(requestDTO.getPushContent());
                                notifiers.notifyUser(NotifyTypeEnum.JS005, notifyDTO);
                            }
                            break;
                            case MISCARRY_CANCER_BORROWER: {
                                notifiers.notifyUser(NotifyTypeEnum.JS014, p);
                            }
                            break;
                            case MISCARRY_LOAN_FAKE_FAILED: {
                                notifiers.notifyUser(NotifyTypeEnum.JS004, p);
                            }
                            break;
                            case MISCARRY_INVEST_OVERTIME: {
                                // 借款人
                                if(!OrgConstant.NIWOTA_ORG_ID.equals(p.getOrgId())) {
                                    notifiers.notifyUser(NotifyTypeEnum.JS011, p);
                                }
                                if(!isZhiNengTou(p.getChannelCode())) {
                                	 // 投资人
                                    notifiers.notifyUser(NotifyTypeEnum.TS002, p);
                                }
                               
                            }
                            break;
                            case MISCARRY_LOAN_BATCH:
                            case MISCARRY_CANCER_MANAGER: {
                                // 借款通知
                                if(!OrgConstant.NIWOTA_ORG_ID.equals(p.getOrgId())) {
                                    ProjectNotifyDTO notifyDTO = new ProjectNotifyDTO();
                                    BeanUtils.copyProperties(p, notifyDTO);
                                    notifyDTO.setPushContent(requestDTO.getPushContent());
                                    notifiers.notifyUser(NotifyTypeEnum.JS012, notifyDTO);
                                }
                                if(!isZhiNengTou(p.getChannelCode())) {
	                                // 投资通知
	                                notifiers.notifyUser(NotifyTypeEnum.TS003, p);
                                }
                            }
                            break;
                            case MISCARRY_AUTO_FAILED: {
                                notifiers.notifyUser(NotifyTypeEnum.JS023, p);
                            }
                            break;
                            case MISCARRY_CREDIT_AMOUNT: {
                                notifiers.notifyUser(NotifyTypeEnum.JS023, p);
                            }
                            break;
                        }

                    }

                    // 天称流标事件
                    AbortiveProjectBackFlowRequestDTO abortiveProjectBackFlowRequestDTO = new AbortiveProjectBackFlowRequestDTO();
                    // 统一使用35,表示系统流标,在天称数据改版前临时方案;
                    abortiveProjectBackFlowRequestDTO.setCause("35");
                    abortiveProjectBackFlowRequestDTO.setBid(String.valueOf(requestDTO.getProjectId()));
                    abortiveProjectBackFlowRequestDTO.setDetail(String.valueOf(requestDTO.getSubStatus().getDesc()));

                    RpcContext.getContext().asyncCall(() -> tianChengDataBackFlowLocalService.abortiveProject(abortiveProjectBackFlowRequestDTO));

                    // 天称标的状态变更事件回流(流标)
                    String fromStatus = null;
                    if (requestDTO.getSubStatus().equals(ProjectSubStatusEnum.MISCARRY_INVEST_OVERTIME)) {
                        fromStatus = String.valueOf(ProjectSubStatusEnum.COLLECTING_INIT.getSubStatus());
                    } else {
                        fromStatus = String.valueOf(ProjectSubStatusEnum.CHECKING_INIT.getSubStatus());
                    }

                    ProjectStatusChangeBackFlowRequestDTO projectStatusChangeBackFlowRequestDTO = new ProjectStatusChangeBackFlowRequestDTO();
                    projectStatusChangeBackFlowRequestDTO.setProjectId(requestDTO.getProjectId());
                    projectStatusChangeBackFlowRequestDTO.setFromStatus(fromStatus);
                    projectStatusChangeBackFlowRequestDTO.setStatus(String.valueOf(requestDTO.getSubStatus().getSubStatus()));

                    RpcContext.getContext().asyncCall(() -> tianChengDataBackFlowLocalService.projecctStatusChange(projectStatusChangeBackFlowRequestDTO));

                    // 流标-释放保证金和授信额度
                    ((ProjectLocalService) (AopContext.currentProxy())).sendNotifyForReleaseCashDepositAndCreditLimit(requestDTO);

                } catch (Exception e) {
                    log.error(String.format("流标###后置处理异常###projectId=%s, errorMessage=%s", p.getProjectId(), e.getMessage()), e);
                }
            }
        });

        return new Tuple2<>(true, backPrize);
    }

    /**
     * 资金端，更新标的状态为坏账还款中
     *
     * @param projectId
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateProjectStatusToBad4Fund(Long projectId) {
        ProjectBorrowerSummary summary = commonUpdateProjectStatusToBad(projectId);
        fundRepaymentService.handPlanPenalty(summary);
    }

    @Transactional
    public ProjectBorrowerSummary commonUpdateProjectStatusToBad(Long projectId) {
        log.info("updateProjectStatusToBad--start--projectId={}", projectId);
        ProjectBorrowerSummary summary = projectBorrowerSummaryMapperExt.selectByPrimaryKey(projectId);
        if (summary.getRepayStatus().equals(RepayStatusEnum.OVERDUE.getCode()) && RepaymentUtil.getOverdueDay(new DateTime(summary.getRefundDate()).plusMonths(1).toDate(), new Date()) > 0) {
            log.info("updateProjectStatusToBad--update--projectId={}", projectId);
            Project projectUpdater = new Project();
            projectUpdater.setProjectId(projectId);
            projectUpdater.setSubStatus(ProjectSubStatusEnum.REPAYING_BAD.getSubStatus());

            int num = projectMapperExt.updateByPrimaryKeySelective(projectUpdater);
            if (num < 1) {
                log.error("更新标的{}状态为坏账还款中失败", projectId);
            }
            SummaryStatusDTO summaryUpdater = new SummaryStatusDTO();
            summaryUpdater.setProjectId(projectId);
            summaryUpdater.setRepayStatus(RepayStatusEnum.BADLOANS.getCode());
            summaryUpdater.setSrcRepayStatus(RepayStatusEnum.OVERDUE.getCode());
            summary.setRepayStatus(summaryUpdater.getRepayStatus());
            int i = projectBorrowerSummaryMapperExt.updateByPrimaryKeyStatusSelective(summaryUpdater);
            if (i == 1) {
                //
                log.info("标的更新成坏账");
            }
        }
        return summary;
    }

    /**
     * 更新标的状态为逾期还款中 只有00：15的自动还款任务分才可以调用
     *
     * @param projectId
     */
    public void updateProjectStatusToOverdue(Long projectId, boolean isGuaranteeJob) {
        if (isGuaranteeJob) {
            ProjectBorrowerSummary summary = ((ProjectLocalService) (AopContext.currentProxy())).commonUpdateProjectStatusToOverdue(projectId);
            // 发送消息到卡牛
            notifyAssetLocalService.sendOverDueMsgToAssert(summary.getProjectId());

        } else {
            log.info("非00：15分自动还款，不可更新标的状态,projectId={}", projectId);
        }
    }

    /**
     * 资金端，更新标的状态为逾期还款中
     *
     * @param projectId
     */
    @Transactional(rollbackFor = Exception.class)
    public void updateProjectStatusToOverdue4Fund(Long projectId) {
        log.info("updateProjectStatusToOverdue--start--projectId={}", projectId);
        ProjectBorrowerSummary summary = projectBorrowerSummaryMapperExt.selectByPrimaryKey(projectId);

        if (summary.getRepayStatus().equals(RepayStatusEnum.NORMAL.getCode())) {
            if (RepaymentUtil.getOverdueDay(summary.getRefundDate(), new Date()) > 0) {
                update2Overdue(projectId, summary);
            }
        } else {
            log.info("projectId={}status is no normal,can't update to overdue..", projectId);
        }
        fundRepaymentService.handPlanPenalty(summary);
    }

    private void update2Overdue(Long projectId, ProjectBorrowerSummary summary) {
        log.info("updateProjectStatusToOverdue--update--projectId={}", projectId);

        Project project = new Project();
        project.setProjectId(projectId);
        project.setSubStatus(ProjectSubStatusEnum.REPAYING_OVERDUE.getSubStatus());
        projectMapperExt.updateByPrimaryKeySelective(project);

        SummaryStatusDTO summaryUpdater = new SummaryStatusDTO();
        summaryUpdater.setProjectId(projectId);
        summaryUpdater.setRepayStatus(RepayStatusEnum.OVERDUE.getCode());
        summaryUpdater.setSrcRepayStatus(RepayStatusEnum.NORMAL.getCode());

        summary.setRepayStatus(summaryUpdater.getRepayStatus());
        int i = projectBorrowerSummaryMapperExt.updateByPrimaryKeyStatusSelective(summaryUpdater);
        if (i == 1) {
            log.info("commonUpdateProjectStatusToOverdue更新标的状态为逾期成功,标的号{}", projectId);
        }
    }

    @Transactional
    public ProjectBorrowerSummary commonUpdateProjectStatusToOverdue(Long projectId) {
        log.info("updateProjectStatusToOverdue--start--projectId={}", projectId);
        ProjectBorrowerSummary summary = projectBorrowerSummaryMapperExt.selectByPrimaryKey(projectId);

        if (summary.getRepayStatus().equals(RepayStatusEnum.NORMAL.getCode())) {
            if (RepaymentUtil.getOverdueDay(summary.getRefundDate(), commonLocalService.getCurrentDate()) >= 0) {
                update2Overdue(projectId, summary);
            }
        } else {
            log.info("projectId={}status is no normal,can't update to overdue..", projectId);
        }
        return summary;
    }

    @Transactional
    public void addProjectDynamicRecord(ProjectDynamicDTO projectDynamicDTO) {
        log.debug("---addProjectDynamicRecord---");
        ProjectDynamic projectDynamic = new ProjectDynamic();
        projectDynamic.setCreateTime(new Date());
        projectDynamic.setId(snowflakeIdWorker.nextId());
        projectDynamic.setProjectId(projectDynamicDTO.getProjectId());
        projectDynamic.setRelationId(projectDynamicDTO.getRelationId());
        projectDynamic.setType(projectDynamicDTO.getTradeTypeEnum().getValue());
        projectDynamic.setUserId(projectDynamicDTO.getUserId());

        List<String> paramsList = projectDynamicDTO.getParamsList();
        if (paramsList != null && paramsList.size() > 0) {
            String oldDesc = projectDynamicDTO.getProjectDynamicEnum().getRemarkTemplate();
            String newDesc = oldDesc;
            for (int i = 0; i < paramsList.size(); i++) {
                newDesc = newDesc.replace("{" + i + "}", paramsList.get(i));
            }
            projectDynamic.setDescription(newDesc);
        } else {
            projectDynamic.setDescription(projectDynamicDTO.getProjectDynamicEnum().getRemarkTemplate());
        }
        projectDynamicMapperExt.insertSelective(projectDynamic);
    }

    /**
     * 人工审核退回, 修改标的状态
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void back(ProjectBackRequestDTO reqDTO) {
        Project updateProject = projectMapperExt.selectByPrimaryKeyForUpdate(reqDTO.getProjectId());
        if (updateProject == null || ProjectStatusEnum.CHECKING.getStatus() != updateProject.getStatus().byteValue()) {
            throw new BizException("TRD20001");
        }
        // 更新标的状态
        updateProject.setStatus(reqDTO.getSubStatus().getParentStatusEnum().getStatus());
        updateProject.setSubStatus(reqDTO.getSubStatus().getSubStatus());
        updateProject.setManualAuditResult(ManualAuditResultEnum.RETURN.getValue());
        // 审核退回流程未结束，不要往标的表写审核时间和审核人
        if (updateProject.getAuditedTime() == null) {
            updateProject.setAuditedTime(new Date());
        }
        updateProject.setAuditManagerId(reqDTO.getManagerId());
        updateProject.setAuditManagerName(reqDTO.getManagerName());
        boolean isUpdateProjectSuccess = false;
        try {
            isUpdateProjectSuccess = projectMapperExt.updateByPrimaryKeySelective(updateProject) > 0;
        } catch (Exception e) {
            log.error("更新标的状态异常，updateProject:{}", JSON.toJSONString(updateProject), e);
            throw new BizException("TRD20002");
        }
        boolean isCreateReturnBorrowRedEnvelopeRecord = false;
        ProjectBackoutBorrowPrize backPrize = null;
        // 创建回退借款红包记录
        if (isUpdateProjectSuccess && updateProject.getPrizeId() != null) {
            backPrize = new ProjectBackoutBorrowPrize();
            backPrize.setProjectId(updateProject.getProjectId());
            backPrize.setUserId(updateProject.getBorrowUserId());
            backPrize.setPrizeId(updateProject.getPrizeId());
            backPrize.setCreateTime(new Date());
            isCreateReturnBorrowRedEnvelopeRecord = projectBackoutBorrowPrizeMapperExt.insertSelective(backPrize) > 0;
        }

    }

    /**
     * 人工审核通过待确认, 修改标的状态
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void confirm(ProjectConfirmRequestDTO reqDTO) {
        Project updateProject = projectMapperExt.selectByPrimaryKeyForUpdate(reqDTO.getProjectId());
        if (updateProject == null || ProjectStatusEnum.CHECKING.getStatus() != updateProject.getStatus().byteValue()) {
            throw new BizException("TRD20001");
        }
        // 更新标的状态
        updateProject.setStatus(reqDTO.getSubStatus().getParentStatusEnum().getStatus());
        updateProject.setSubStatus(reqDTO.getSubStatus().getSubStatus());
        updateProject.setAuditedTime(new Date());
        updateProject.setManualAuditResult(ManualAuditResultEnum.PASS.getValue());
        updateProject.setAuditManagerId(reqDTO.getManagerId());
        updateProject.setAuditManagerName(reqDTO.getManagerName());
        projectMapperExt.updateByPrimaryKeySelective(updateProject);
    }

    public Project selectByUserIdAndProjectId(String userId, Long projectId) {
        return projectMapperExt.selectByUserIdAndProjectId(userId, projectId);
    }

    /**
     * 查询R360审核通过未确认的标的列表
     *
     * @param timeout 超时时间（单位：小时），审核通过后确认时间
     * @return
     */
    public List<Project> getR360TimeoutUnconfirmedProjectList(Integer timeout) {
        return projectMapperExt.selectR360TimeoutUnconfirmedProjectList(timeout);
    }

    public Integer countProjectNumInApproveAndInvest(String userId, ProjectFromSourceEnum fromSource) {
        return projectMapperExt.queryProjectNumInApproveAndInvestWithSource(userId, fromSource);
    }

    /**
     * 查询审核中的借款笔数
     *
     * @param userId
     * @return
     */
    public Integer queryInAuditProjectCount(String userId) {
        return projectMapperExt.selectInAuditProjectCount(userId);
    }

    /**
     * 查询提交借款笔数
     *
     * @param userId
     * @return
     */
    public Integer queryProjectCountByUserIdAndOrgId(String userId,String orgId) {
        return projectMapperExt.selectProjectCountByUserIdAndOrgId(userId,orgId);
    }

    /**
     * 查询提交借款笔数
     *
     * @param userId
     * @return
     */
    public Integer queryProjectCountByUserId(String userId) {
        return projectMapperExt.selectProjectCountByUserId(userId);
    }

    /**
     * 查询最近一次借款记录
     *
     * @param userId
     * @return
     */
    public Project queryLastProjectByUserId(String userId) {
        return projectMapperExt.selectLastOneByUserId(userId);
    }

    /**
     * 查询标的总利息
     *
     * @param projectId
     * @return
     */
    public BigDecimal queryTotalInterestByProjectId(Long projectId) {
        return projectBorrowerPlanDetailMapperExt.countTotalMoneyByProjectIdAndFeeType(projectId, FeeTypeEnum.INTEREST.getValue());
    }

    /**
     * 查询借款中的标的数量
     *
     * @param userId
     * @return
     */
    public Integer queryLoaningProjectCount(String userId) {
        return projectMapperExt.selectLoaningProjectCount(userId);
    }

    /**
     * 查询逾期还款计划摘要
     *
     * @param projectId
     * @return
     */
    public ProjectBorrowerSummary queryDueProjectBorrowerSummaryByProjectId(Long projectId) {
        return projectBorrowerSummaryMapperExt.selectDueByProjectId(projectId);
    }

    /**
     * 查询用户成功借款次数
     *
     * @param userId
     * @return
     */
    public Integer queryLoanSuccessCount(String userId) {
        return projectMapperExt.selectLoanSuccessCount(userId);
    }

    /**
     * 查询用户成功借款次数
     *
     * @param projectId
     * @return
     */
    public CurrentPeriodToReturnDTO queryCurrentPeriodToReturnByProjectId(Long projectId) {
        // 调用计算服务获取当前应还金额
        RefundStateDTO refundStateDTO = feeCalcForRefundService.caculateAmountRepaymentForAPP(projectId, RefundTypeEnum.IMMEDIATELY);

        // 构建返回对象
        CurrentPeriodToReturnDTO currentPeriodToReturnDTO = new CurrentPeriodToReturnDTO();

        currentPeriodToReturnDTO.setCurrentPeriodToReturnCapital(refundStateDTO.getCapital());
        currentPeriodToReturnDTO.setCurrentPeriodToReturnInterest(refundStateDTO.getInterest());
        currentPeriodToReturnDTO.setCurrentPeriodToReturnPenaltyFee(refundStateDTO.getPenaltyFee());
        currentPeriodToReturnDTO.setCurrentPeriodToReturnManagementFee(refundStateDTO.getManageFee());
        currentPeriodToReturnDTO.setCurrentPeriodToReturnGuaranteeFee(refundStateDTO.getGuaranteeFee());
        return currentPeriodToReturnDTO;
    }

    /**
     * 查询符合状态的卡牛的标的
     *
     * @param userId
     * @param subStatus
     * @return
     */
    public Integer findProjectByStatus(String userId, List<Byte> subStatus) {
        return projectMapperExt.findProjectByStatus(userId, subStatus);
    }

	/**
	 * 卡牛标的初始化
	 */
	@Transactional
	public Long cardNiuProject(CardniuProjectDTO cardniuProjectDTO) {
		Project project = new Project();
		project.setProjectId(snowflakeIdWorker.nextId());
		project.setBorrowUserId(cardniuProjectDTO.getUserId());
		project.setBorrowUserName(cardniuProjectDTO.getUserName());
		project.setBorrowMobile(cardniuProjectDTO.getMobile());
		project.setBorrowIdCard(cardniuProjectDTO.getIdCard());
		project.setBorrowHeadImage(cardniuProjectDTO.getHeadImage());
		project.setBorrowNickName(cardniuProjectDTO.getNickname());
		project.setBorrowGender(cardniuProjectDTO.getGender());
		project.setRepaymentType(cardniuProjectDTO.getRepaymentType());
		project.setDeadline(cardniuProjectDTO.getDeadLine());
		project.setDeadlineUnit(cardniuProjectDTO.getDeadlineUnit());
		project.setSubStatus(cardniuProjectDTO.getSubStatus());
		project.setType(cardniuProjectDTO.getType());
		project.setTitle(cardniuProjectDTO.getTitle());
		project.setHandAmount(cardniuProjectDTO.getHandAmount());
		project.setFromSource(cardniuProjectDTO.getFromSource());
		project.setCreditType(cardniuProjectDTO.getCreditType());
        project.setLoanType(ProjectCreditTypeEnum.typeOf(project.getCreditType()).getLoanTypeEnum().getValue());
//		project.setChannelCode(ChannelCodeEnum.APP.getChanelCode());
		project.setCreateTime(new Date());
		project.setHandAmount(cardniuProjectDTO.getHandAmount());
		project.setContractAmount(cardniuProjectDTO.getHandAmount());
		NiiwooScoreResponseDTO scoreResponseDTO = userBasicInfoDubboService.selectUserNiiwooScore(cardniuProjectDTO.getUserId());
		if (scoreResponseDTO != null) {
			project.setBorrowNiiwooScore(scoreResponseDTO.getScore());
		}
		RateConfig rateConfig = rateConfigMapperExt.queryRateConfigByDeadline(cardniuProjectDTO.getDeadLine(), ProjectTypeEnum.CARD_NIU_LOAN.getValue());
		if (rateConfig != null) {
			project.setBorrowRate(rateConfig.getBorrowInterestRate());
			project.setManageRate(rateConfig.getPlatformManageFeeRate());
			project.setGuaranteeRate(rateConfig.getGuaranteeServiceFeeRate());
			project.setRepaymentType(rateConfig.getRepaymentMode());
			project.setDeadline(rateConfig.getDeadline());
			project.setDeadlineUnit(new Integer(1).byteValue());

            BigDecimal platformManageFee = cardniuProjectDTO.getHandAmount().multiply(rateConfig.getPlatformManageFeeRate()).setScale(PRECISION_TWO, BigDecimal.ROUND_HALF_UP);
            BigDecimal guaranteeServiceFee = cardniuProjectDTO.getHandAmount().multiply(rateConfig.getGuaranteeServiceFeeRate()).setScale(PRECISION_TWO, BigDecimal.ROUND_HALF_UP);
            //设置平台管理费和担保服务费用
            project.setManageFee(platformManageFee);
            project.setGuaranteeFee(guaranteeServiceFee);
        }
		List<TradeConfigEnum> enums = new ArrayList<>();
		enums.add(TradeConfigEnum.CARDNIU_OVERDUEFEERATE);
		enums.add(TradeConfigEnum.CARDNIU_INVESTDEADLINE);
		Map<String, String> configMap = tradeConfigLocalService.selectTradeConfigByEmuns(enums);
		if (configMap != null) {
			project.setPenaltyRate(new BigDecimal(configMap.get(TradeConfigEnum.CARDNIU_OVERDUEFEERATE.getConfigKey())));
		}
		CardniuUserDeviceInfoDTO cardniuUserDeviceInfoDTO = loanDubboService.userDeviceInfo(cardniuProjectDTO.getUserId());
		project.setLongitude(cardniuUserDeviceInfoDTO.getLongitude());
		project.setLatitude(cardniuUserDeviceInfoDTO.getLatitude());
		project.setLoanProvince(cardniuUserDeviceInfoDTO.getProvince());
		project.setLoanCity(cardniuUserDeviceInfoDTO.getCity());
		project.setLoanArea(cardniuUserDeviceInfoDTO.getCountry());
		project.setOrgId(OrgConstant.NIIWOO_ORG_ID);
		AccountBaseResponse accountBaseResponse = accountDubboService.loadNiiwooAccount(cardniuProjectDTO.getUserId(), UserRoleEnum.BORROWERS);
		project.setBorrowAccountId(accountBaseResponse.getAccountId());
		project.setBorrowPlatformUserNo(accountBaseResponse.getAccountNo());
		project.setBorrowerType(BorrowerTypeEnum.PERSONAL.getType());
        project.setIsMasterBorrower((byte) 1);

        // 初次申请金额、期限
        project.setFirstDeadline(project.getDeadline());
        project.setFirstDeadlineUnit(project.getDeadlineUnit());
        project.setFirstAmount(project.getContractAmount());

        projectMapperExt.insertSelective(project);

        SpeedLoanApplyRequestDTO speedLoanApplyRequestDTO = new SpeedLoanApplyRequestDTO();
		SpeedLoanApplyRequestDTO.DeviceInfoRequestDTO deviceInfoRequestDTO = new SpeedLoanApplyRequestDTO.DeviceInfoRequestDTO();
		deviceInfoRequestDTO.setOs(cardniuUserDeviceInfoDTO.getOs());
		deviceInfoRequestDTO.setIp(cardniuUserDeviceInfoDTO.getIp());
		deviceInfoRequestDTO.setBrandType(cardniuUserDeviceInfoDTO.getBrand());

		String os = cardniuUserDeviceInfoDTO.getOs();
		if (os != null && !"".equals(os)) {
			if ("ios".equals(os)) {
				deviceInfoRequestDTO.setDeviceId(cardniuUserDeviceInfoDTO.getIdfa());
			} else if ("android".equals(os)) {
				deviceInfoRequestDTO.setDeviceId(cardniuUserDeviceInfoDTO.getImei());
			}
		}
		deviceInfoRequestDTO.setGps(cardniuUserDeviceInfoDTO.getProvince() + cardniuUserDeviceInfoDTO.getCity() + cardniuUserDeviceInfoDTO.getCountry() + cardniuUserDeviceInfoDTO.getStreet()
				+ cardniuUserDeviceInfoDTO.getStreetNumber());
		if (!StringUtils.isEmpty(cardniuUserDeviceInfoDTO.getJailBreak()) && cardniuUserDeviceInfoDTO.getJailBreak().equals("否")) {

            deviceInfoRequestDTO.setDeviceStatus(new Integer(0).byteValue());
        } else if (!StringUtils.isEmpty(cardniuUserDeviceInfoDTO.getJailBreak()) && cardniuUserDeviceInfoDTO.getJailBreak().equals("是")) {
            deviceInfoRequestDTO.setDeviceStatus(new Integer(1).byteValue());
        }
        speedLoanApplyRequestDTO.setUserId(cardniuProjectDTO.getUserId());
        speedLoanApplyRequestDTO.setDeviceInfo(deviceInfoRequestDTO);
        // 插入设备信息
        speedLoanService.projectLoanDeviceInfoCreat(speedLoanApplyRequestDTO, project);
        // 插入标的费率数据
        speedLoanService.cardniuProjectRateCreat(cardniuProjectDTO.getUserId(), project.getProjectId(), cardniuProjectDTO.getDeadLine());

        // 记录标的动态信息
        ProjectDynamicDTO projectDynamicDTO = new ProjectDynamicDTO();
        projectDynamicDTO.setUserId(project.getBorrowUserId());
        projectDynamicDTO.setProjectId(project.getProjectId());
        projectDynamicDTO.setTradeTypeEnum(ProjectDynamicDTO.TradeTypeEnum.REPAYMENT);
        projectDynamicDTO.setProjectDynamicEnum(ProjectDynamicEnum.APPLY_SUBMIT_SUCCESS);
        addProjectDynamicRecord(projectDynamicDTO);

        // 生命周期
        ProjectLifeDTO projectLifeDTO = new ProjectLifeDTO();
        projectLifeDTO.setProjectId(project.getProjectId());
        projectLifeDTO.setType(ProjectLifeCycleEnum.USER_APPLY.getType());
        projectLifeDTO.setDesc(ProjectLifeCycleEnum.enumOf(ProjectLifeCycleEnum.USER_APPLY.getType()));
        projectLifeDTO.setCurrentTime(new Date());
        // rabbitTemplate.convertAndSend(RabbitConstant.Exchange.PROJECT_LIFE_CYCLE,
        // RabbitConstant.RoutingKey.PROJECT_LIFE_CYCLE,projectLifeDTO);

        return project.getProjectId();
    }

    /**
     * 查询不通过原因
     *
     * @param borrowerUserId
     * @param projectId
     */
    public List<String> selectNotPassCause(String borrowerUserId, Long projectId) {
        List<String> thirdReasonCodes = new ArrayList<>();
        try {
            ProjectAuditDecision projectAuditDecision = projectAuditDecisionMapperExt.selectForNotPass(borrowerUserId, projectId);
            if (projectAuditDecision != null) {
                String auditReason = projectAuditDecision.getAuditReason();
                String[] reasonCodes = auditReason.split("\\|");
                if (reasonCodes != null && reasonCodes.length > 0) {
                    List<AuditSource> auditSources = auditSourceMapperExt.queryThirdCodes(Arrays.asList(reasonCodes), ProjectTypeEnum.CARD_NIU_LOAN.getValue());
                    if (!auditSources.isEmpty()) {
                        for (AuditSource auditSource : auditSources) {
                            String reasonCode = auditSource.getThirdReasonCode();
                            if (StringUtils.hasText(auditReason)) {
                                thirdReasonCodes.add(reasonCode);
                            }
                        }
                    }
                }
            }
            log.info("卡牛借款结果查询审核不通过原因 borrowerUserId={} projectId={} thirdReasonCodes={}", borrowerUserId, projectId, thirdReasonCodes);
        } catch (Exception e) {
            log.error("卡牛借款结果查询审核不通过原因【查询异常】borrowerUserId={} projectId={}", borrowerUserId, projectId, e);
        }
        return thirdReasonCodes;
    }

    /**
     * 查询用户指定状态的标的个数
     */
    public Integer selectProjectCountBySubStatus(String userId, List<Integer> subStatus) {

        return projectMapperExt.selectProjectCountBySubStatus(userId, subStatus);
    }

    /**
     * 查询符合项目状态的标的个数
     */
    public Integer queryProjectByStatus(String userId, List<ProjectStatusEnum> statusEnumList) {

        return projectMapperExt.queryProjectByStatus(userId, statusEnumList);
    }

    /**
     * 添加机构标的
     *
     * @param projectDTO
     * @return
     */
    @Transactional
    public void addOrgProject(OrgProjectDTO projectDTO) {
        log.info("机构上标,订单编号={},请求参数={}",projectDTO==null?null:(projectDTO.getOrderId()==null?null:projectDTO.getOrderId()),JSON.toJSONString(projectDTO));

        AssetOrgProject assetOrgProject = assetOrgProjectMapperExt.selectByOrgIdAndOrderId(projectDTO.getOrgId(), projectDTO.getOrderId());
        if (assetOrgProject != null) {
            log.info("机构上标, 标的已创建，OrderId={}，AssetOrgProject={}", projectDTO == null ? null : (projectDTO.getOrderId() == null ? null : projectDTO.getOrderId()), JSON.toJSONString(assetOrgProject));
            return;
        }

        Project project = new Project();

		// 标的基本信息
		project.setProjectId(snowflakeIdWorker.nextId());
		project.setOrgId(projectDTO.getOrgId());
		project.setBorrowAccountId(projectDTO.getBorrowAccountId());
		project.setBorrowPlatformUserNo(projectDTO.getBorrowPlatformUserNo());
		project.setBorrowUserId(projectDTO.getUserId());
		project.setBorrowUserName(projectDTO.getUserName());
		project.setBorrowMobile(projectDTO.getMobile());
		project.setBorrowIdCard(projectDTO.getIdCard());
		project.setBorrowHeadImage(projectDTO.getHeadImage());
		project.setBorrowNickName(projectDTO.getNickname());
		project.setBorrowGender(projectDTO.getGender());
		project.setRepaymentType(projectDTO.getRepaymentType());
		project.setDeadline(projectDTO.getDeadLine());
		project.setDeadlineUnit(projectDTO.getDeadlineUnit());
		project.setStatus(projectDTO.getStatus());
		project.setSubStatus(projectDTO.getSubStatus());
		project.setType(projectDTO.getType());
		project.setTitle(projectDTO.getTitle());
		project.setFromSource(projectDTO.getFromSource());
		project.setCreditType(projectDTO.getCreditType());
		project.setHandAmount(projectDTO.getAmount());
		project.setContractAmount(projectDTO.getAmount());
		project.setBorrowNiiwooScore(projectDTO.getScore());
		project.setBorrowerType(projectDTO.getBorrowerType());
		project.setLoanCity(projectDTO.getLoanCity());
		project.setLoanArea(projectDTO.getLoanArea());
        project.setLoanType(projectDTO.getIsMasterBorrower().equals(ProjectIsMasterBorrowerEnum.YES.getValue())?ProjectCreditTypeEnum.typeOf(projectDTO.getCreditType()).getLoanTypeEnum().getValue():LoanTypeEnum.OTHER_LOAN.getValue());
		project.setCreateTime(new Date());
        project.setBeforeSeekAdviceServiceFeeRate(projectDTO.getBeforeSeekAdviceServiceFeeRate().divide(new BigDecimal(100)));
        project.setBeforeSeekAdviceServiceFee(project.getContractAmount().multiply(project.getBeforeSeekAdviceServiceFeeRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN));
        project.setAfterSeekAdviceServiceFeeChargeNode(projectDTO.getAfterSeekAdviceServiceFeeChargeNode());
        project.setGuaranteeFeeChargeNode(projectDTO.getGuaranteeServiceFeeChargeNode());
		project.setConsultingRate(projectDTO.getConsultingRate());//咨询服务费率
		project.setConsultingBusinessLicense(projectDTO.getConsultingBusinessLicense());//分润公司营业执照编号
        project.setBorrowEmail(projectDTO.getBorrowEmail());
        BigDecimal consultingFee = project.getContractAmount().multiply(project.getConsultingRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        project.setConsultingFee(consultingFee);
        project.setIsMasterBorrower(projectDTO.getIsMasterBorrower());

		// 极速借机构添加标的时设置提交时间
		if (ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(projectDTO.getType())) {
		    project.setSubmitTime(project.getCreateTime());
        }

        // 费率相关
        project.setBorrowRate(projectDTO.getBorrowInterestRate());
		project.setBeforeManageRate(projectDTO.getBeforePlatformManageFeeRate());
        project.setManageRate(projectDTO.getPlatformManageFeeRate());
        project.setGuaranteeRate(projectDTO.getGuaranteeServiceFeeRate());
        project.setRepaymentType(projectDTO.getRepaymentType());

        //设置平台管理费和担保服务费用
        BigDecimal beforePlatformManageFee = project.getContractAmount().multiply(project.getBeforeManageRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        BigDecimal afterPlatformManageFee = project.getContractAmount().multiply(project.getManageRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN);
        BigDecimal guaranteeServiceFee = project.getContractAmount().multiply(project.getGuaranteeRate()).setScale(2, BigDecimal.ROUND_HALF_EVEN);

        project.setBeforeManageFee(beforePlatformManageFee);
        project.setManageFee(afterPlatformManageFee);
        project.setGuaranteeFee(guaranteeServiceFee);
        project.setManageFeeChargeNode(projectDTO.getPlatformManageFeeChargeNode());

        // 罚息费率
        String penaltyRateStr = tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.OVERDUEFEERATE);
        project.setPenaltyRate(new BigDecimal(penaltyRateStr));

        // 初次申请金额、期限
        project.setFirstDeadline(project.getDeadline());
        project.setFirstDeadlineUnit(project.getDeadlineUnit());
        project.setFirstAmount(project.getContractAmount());

        projectMapperExt.insertSelective(project);

        // 平台管理费担保服务费费率配置
        addOrgProjectRate(project,projectDTO.getAfterSeekAdviceServiceFeeRateList(), projectDTO.getAfterPlatformManageFeeRatioList());

        // 记录标的动态信息
        ProjectDynamicDTO projectDynamicDTO = new ProjectDynamicDTO();
        projectDynamicDTO.setUserId(project.getBorrowUserId());
        projectDynamicDTO.setProjectId(project.getProjectId());
        projectDynamicDTO.setTradeTypeEnum(ProjectDynamicDTO.TradeTypeEnum.REPAYMENT);
        projectDynamicDTO.setProjectDynamicEnum(ProjectDynamicEnum.APPLY_SUBMIT_SUCCESS);
        addProjectDynamicRecord(projectDynamicDTO);

		// 插入机构标的信息
		AssetOrgProject orgProject = new AssetOrgProject();
		orgProject.setProjectId(project.getProjectId());
		orgProject.setOrgId(projectDTO.getOrgId());
		orgProject.setOrderId(projectDTO.getOrderId());
		orgProject.setBorrowerType(project.getBorrowerType());
		orgProject.setBorrowUserId(project.getBorrowUserId());
		orgProject.setProjectStatus(project.getStatus());
		orgProject.setSubStatus(project.getSubStatus());
		orgProject.setAmount(project.getContractAmount());
		orgProject.setBorrowerMobile(project.getBorrowMobile());
		orgProject.setBorrowerName(project.getBorrowUserName());
		orgProject.setApplyTime(new Date());
		assetOrgProjectMapperExt.insertSelective(orgProject);

		// 生成标的快照mq
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("projectId", project.getProjectId());
        jsonObject.put("borrowerUserId", project.getBorrowUserId());
        jsonObject.put("productType", project.getType());
        log.info("机构推标,标的ID={},发送标的快照MQ开始,请求参数={}", project.getProjectId(), JSON.toJSONString(jsonObject));
        rabbitTemplate.convertAndSend(MqConstant.CREATE_PROJECT_SNAPSHOT_EXCHANGE, MqConstant.CREATE_PROJECT_SNAPSHOT_ROUTE, JSON.toJSONString(jsonObject));
	}

    /**
     * 保存极速借机构费率配置
     * @param project
     */
    private void addOrgProjectRate(Project project,List<BigDecimal> afterSeekAdviceServiceFeeRateList, List<BigDecimal> afterPlatformManageFeeRatioList) {
        if (DeadlineUnitEnum.BY_DAY.getDeadlineUnit().equals(project.getDeadlineUnit())) {
            ProjectRate projectRate = new ProjectRate();
            projectRate.setId(snowflakeIdWorker.nextId());
            projectRate.setCurrentPeriod(1);
            projectRate.setGuaranteeServiceFeePercent(BigDecimal.ONE);
            projectRate.setPlatformManageFeePercent(BigDecimal.ONE);
            projectRate.setBorrowUserId(project.getBorrowUserId());
            projectRate.setProjectId(project.getProjectId());
            projectRate.setCreateTime(new Date());
            projectRateMapper.insertSelective(projectRate);
        } else {
            List<ProjectRate> projectRateList = new ArrayList<ProjectRate>();
            for (int i = 0; i < project.getDeadline(); i++) {
                ProjectRate projectRate = new ProjectRate();
                projectRate.setId(snowflakeIdWorker.nextId());
                projectRate.setCurrentPeriod(i + 1);
                projectRate.setBorrowUserId(project.getBorrowUserId());
                projectRate.setProjectId(project.getProjectId());
                projectRate.setCreateTime(new Date());

                projectRateList.add(projectRate);
            }

            //1.担保服务费率划分;
            //1.1 放款时
            if (project.getGuaranteeFeeChargeNode().equals(GuaranteeFeeChargeNodeEnum.MAKE_LOAN.getNode())) {
                for (int i = 0; i < project.getDeadline(); i++) {
                    projectRateList.get(i).setGuaranteeServiceFeePercent(BigDecimal.ZERO);
                }
            }
            //1.2 还款第一期
            else if (project.getGuaranteeFeeChargeNode().equals(GuaranteeFeeChargeNodeEnum.REPAY_FIRST_PERIOD.getNode())) {
                for (int i = 0; i < project.getDeadline(); i++) {
                    if (i == 0) {
                        projectRateList.get(0).setGuaranteeServiceFeePercent(BigDecimal.ONE);
                    } else {
                        projectRateList.get(i).setGuaranteeServiceFeePercent(BigDecimal.ZERO);
                    }
                }
            }
            //1.3 还款每期
            else if (project.getGuaranteeFeeChargeNode().equals(GuaranteeFeeChargeNodeEnum.REPAY_PER_OERIOD.getNode())) {
                BigDecimal perPeriodFeePercent = BigDecimal.ONE.divide(new BigDecimal(project.getDeadline().toString()), 6, BigDecimal.ROUND_HALF_UP);
                BigDecimal lastPeriodFeePercent = BigDecimal.ONE.subtract(perPeriodFeePercent.multiply(new BigDecimal((project.getDeadline() - 1) + "")));
                for (int i = 0; i < project.getDeadline(); i++) {
                    if (i == project.getDeadline() - 1) {
                        projectRateList.get(i).setGuaranteeServiceFeePercent(lastPeriodFeePercent);
                    } else {
                        projectRateList.get(i).setGuaranteeServiceFeePercent(perPeriodFeePercent);
                    }
                }
            }

            //2.平台管理费率划分;
            for (int i = 0; i < project.getDeadline(); i++) {
                projectRateList.get(i).setPlatformManageFeePercent(afterPlatformManageFeeRatioList.get(i));
            }

            //3.后置咨询服务费率划分;
            //3.1放款时
            if (project.getAfterSeekAdviceServiceFeeChargeNode().equals(AfterSeekAdviceFeeChargeNodeEnum.MAKE_LOAN.getNode())) {
                for (int i = 0; i < project.getDeadline(); i++) {
                    projectRateList.get(i).setAfterSeekAdviceServiceFeeRate(BigDecimal.ZERO);
                }
            }
            //3.2 还款第一期
            else if (project.getAfterSeekAdviceServiceFeeChargeNode().equals(AfterSeekAdviceFeeChargeNodeEnum.REPAY_FIRST_PERIOD.getNode())) {
                for (int i = 0; i < project.getDeadline(); i++) {
                    if (i == 0) {
                        projectRateList.get(0).setAfterSeekAdviceServiceFeeRate(afterSeekAdviceServiceFeeRateList.get(0).divide(new BigDecimal(100).setScale(2,BigDecimal.ROUND_HALF_EVEN)));
                    } else {
                        projectRateList.get(i).setAfterSeekAdviceServiceFeeRate(BigDecimal.ZERO);
                    }
                }
            }
            //3.3 还款每期
            else if (project.getAfterSeekAdviceServiceFeeChargeNode().equals(AfterSeekAdviceFeeChargeNodeEnum.REPAY_PER_OERIOD.getNode())) {
                for (int i = 0; i < project.getDeadline(); i++) {
                    projectRateList.get(i).setAfterSeekAdviceServiceFeeRate(afterSeekAdviceServiceFeeRateList.get(i).divide(new BigDecimal(100).setScale(2,BigDecimal.ROUND_HALF_EVEN)));
                }
            }

            for (ProjectRate projectRate : projectRateList) {
                projectRateMapper.insertSelective(projectRate);
            }
        }
    }

    /**
	 * 机构标推送存管
	 *
	 * @param orgId
	 * @param orderId
	 */
	public void pushOrgProject(String orgId, Long orderId) {
		if (!HintManagerHolder.isMasterRouteOnly()) {
			HintManagerHolder.clear();
			HintManager hintManager = HintManager.getInstance();
			hintManager.setMasterRouteOnly();
		}
		AssetOrgProject assetOrgProject = assetOrgProjectMapperExt.selectByOrgIdAndOrderId(orgId, orderId);

        PushProjectRequestDTO pushProjectRequestDTO = new PushProjectRequestDTO();
        pushProjectRequestDTO.setAuditedTime(new Date());
        pushProjectRequestDTO.setProjectId(assetOrgProject.getProjectId());
        pushProjectRequestDTO.setPushFromEnum(PushProjectRequestDTO.PushFromEnum.AUTO_AUDIT);

        Project project = projectCommonService.queryProjectBasicInfo(pushProjectRequestDTO.getProjectId());
        ISpeedLoanService service = speedLoanFactory.adaptive(project);
        service.pushProject(pushProjectRequestDTO);
    }

    /**
     * 流标-释放保证金和授信额度
     *
     * @param requestDTO
     */
    public void sendNotifyForReleaseCashDepositAndCreditLimit(ProjectFlowRequestDTO requestDTO) {
        try {
            AssetOrgProjectResponseDTO asserOrgProject = orgProjectLocalService.getAsserOrgProject(requestDTO.getProjectId());
            if (asserOrgProject != null && asserOrgProject.getProjectId() != null) {
                Boolean isNeedSendMq = false;
                switch (requestDTO.getSubStatus()) {
                    case MISCARRY_AUTO_FAILED:
                        // 流标-自动审核不通过
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_MANUAL_FAILED:
                        // 流标-人工审核不通过
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_BACK_OVERTIME:
                        // 流标-借款退回超时未处理
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_CANCER_BORROWER:
                        // 流标-借款人手动取消
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_INVEST_OVERTIME:
                        // 流标-投资超时
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_CANCER_MANAGER:
                        // 流标-管理人员手动取消
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_LOAN_FAKE_FAILED:
                        // 流标-借款反欺诈不通过
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_LOAN_BATCH:
                        // 管理员批量流标
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_PUSH_FAIL:
                        // 存管上标失败
                        isNeedSendMq = true;
                        break;
                    case MISCARRY_CREDIT_AMOUNT:
                        // 流标-授信额度低于起借金额
                        isNeedSendMq = true;
                        break;

                }
                if (isNeedSendMq) {
                    ProjectStatusChangeDTO dto = new ProjectStatusChangeDTO();
                    dto.setProjectId(requestDTO.getProjectId());

                    rabbitTemplate.convertAndSend(OrgRabbitConstant.Exchange.ORG_COMMON_DIRECT_EXCHANGE,
                            MqConstants.getRoutingKey(OrgRabbitConstant.Exchange.ORG_COMMON_DIRECT_EXCHANGE, OrgRabbitConstant.Queue.PROJECT_STATUS_CHANGE_NOTIFY_QUEUE), dto);

                    log.info("流标###流标释放保证金和授信额度发送mq###data={}", requestDTO.getProjectId());
                }
            }
        } catch (Exception e) {
            log.error("流标释放保证金和授信额度异常sendNotifyForReleaseCashDepositAndCreditLimit:projectId=" + requestDTO.getProjectId(), e);
        }
    }

    /**
     * 查询订单的冻结参数
     *
     * @param orderId
     * @return
     */
    public RefundFreezeParamResponseDTO queryRefundFreezeParam(Long orderId) {
        RefundOrder order = refundOrderMapperExt.selectByPrimaryKey(orderId);
        if (Objects.isNull(order)) {
        	log.warn("传递的订单ID获取不到信息orderId{}", orderId);
            throw new IllegalArgumentException("传递的订单ID获取不到信息");
        }
        Project project = projectMapperExt.selectByPrimaryKey(order.getProjectId());
        if (Objects.isNull(project)) {
            throw new IllegalArgumentException("传递的订单ID获取不到标的信息");
        }
        RefundFreezeParamResponseDTO result = new RefundFreezeParamResponseDTO();
        result.setOrderId(order.getId());
        result.setOrderTypeEnum(RefundEnumConverter.orderTypeEnumConvert(order.getTradeType()).getCode());
        OrderRefundTypeEnum orderRefundTypeEnum= RefundEnumConverter.orderRefundTypeEnumConvert(order.getRefundType(), order.getTradeType(), order.getRepayStatus());
        result.setOrderRefundTypeEnum(orderRefundTypeEnum==null?null:orderRefundTypeEnum.getRefundType());
        result.setProjectId(order.getProjectId());
        result.setBorrowProjectTypeEnum(BorrowProjectTypeEnum.enumOf(project.getType()));
        result.setProjectTitle(project.getTitle());
        if (DeadlineUnitEnum.BY_MONTH.getDeadlineUnit().equals(project.getDeadlineUnit())) {
            ProjectBorrowerSummary summary = projectBorrowerSummaryMapperExt.selectByPrimaryKey(order.getProjectId());
            result.setCurrentPeriod(summary.getCurrentPeriod());
            result.setTotalPeriods(summary.getTotalPeriod());
        }
        result.setRepaymentAccountId(order.getSourceAccountId());
        result.setAmount(order.getAmount());
        List<BaseFeeDetailDTO> feeDetailDTOList = new ArrayList<>();
        result.setFeeDetailDTOList(feeDetailDTOList);
        if (order.getCapital().compareTo(BigDecimal.ZERO) > 0) {
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.CAPITAL);
            dto.setAmount(order.getCapital());
            feeDetailDTOList.add(dto);
        }

        if (order.getInterest().compareTo(BigDecimal.ZERO) > 0) {
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.INTEREST);
            dto.setAmount(order.getInterest());
            feeDetailDTOList.add(dto);
        }

        if (order.getPenaltyFee().compareTo(BigDecimal.ZERO) > 0) {
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.PENALTY);
            dto.setAmount(order.getPenaltyFee());
            feeDetailDTOList.add(dto);
        }

        if (order.getGuaranteeFee().compareTo(BigDecimal.ZERO) > 0) {
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.GUARANTEE_FEE);
            dto.setAmount(order.getGuaranteeFee());
            feeDetailDTOList.add(dto);
        }

        if (order.getManageFee().compareTo(BigDecimal.ZERO) > 0) {
            BaseFeeDetailDTO dto = new BaseFeeDetailDTO();
            dto.setFeeTypeEnum(FeeTypeEnum.MANAGEMENT_FEE);
            dto.setAmount(order.getManageFee());
            feeDetailDTOList.add(dto);
        }
        return result;
    }

    public void updateProjectStatus(Long projectId) {
        speedLoanService.updateProjectStatus(projectId, null, ProjectSubStatusEnum.CHECKING_AUTO_MANUAL);
    }
    /**
     * 统计今日审核通过的标的数量
     *
     * @return
     */
    public Integer countTodayAuditPassProject(){
        Date startDate = DateUtils.parseDateFromString(DateUtils.getDateStart(new Date()),DateUtils.SHOW_DATETIME_FORMAT);
        Date endDate = DateUtils.parseDateFromString(DateUtils.getDateEnd(new Date()),DateUtils.SHOW_DATETIME_FORMAT);
        return projectMapperExt.countTodayAuditPassProject(startDate,endDate);
    }

    @Transactional
    public Long fastLoanProject(FastLoanProjectDTO fastLoanProjectDTO) {
        Project project = new Project();
        project.setProjectId(snowflakeIdWorker.nextId());
        project.setBorrowUserId(fastLoanProjectDTO.getUserId());
        project.setBorrowUserName(fastLoanProjectDTO.getUserName());
        project.setBorrowMobile(fastLoanProjectDTO.getMobile());
        project.setBorrowIdCard(fastLoanProjectDTO.getIdCard());
        project.setBorrowHeadImage(fastLoanProjectDTO.getHeadImage());
        project.setBorrowNickName(fastLoanProjectDTO.getNickname());
        project.setBorrowGender(fastLoanProjectDTO.getGender());
        project.setRepaymentType(fastLoanProjectDTO.getRepaymentType());
        project.setDeadline(fastLoanProjectDTO.getDeadLine());
        project.setDeadlineUnit(fastLoanProjectDTO.getDeadlineUnit());
        project.setSubStatus(fastLoanProjectDTO.getSubStatus());
        project.setType(fastLoanProjectDTO.getType());
        project.setTitle(fastLoanProjectDTO.getTitle());
        project.setHandAmount(fastLoanProjectDTO.getHandAmount());
        project.setFromSource(fastLoanProjectDTO.getFromSource());
        project.setCreditType(fastLoanProjectDTO.getCreditType());
        project.setLoanType(ProjectCreditTypeEnum.typeOf(project.getCreditType()).getLoanTypeEnum().getValue());
//        project.setChannelCode(ChannelCodeEnum.APP.getChanelCode());
        project.setCreateTime(new Date());
        project.setHandAmount(fastLoanProjectDTO.getHandAmount());
        project.setContractAmount(fastLoanProjectDTO.getHandAmount());
        NiiwooScoreResponseDTO scoreResponseDTO = userBasicInfoDubboService.selectUserNiiwooScore(fastLoanProjectDTO.getUserId());
        if (scoreResponseDTO != null) {
            project.setBorrowNiiwooScore(scoreResponseDTO.getScore());
        }
        RateConfig rateConfig = rateConfigMapperExt.queryRateConfigByDeadline(fastLoanProjectDTO.getDeadLine(), fastLoanProjectDTO.getType());
        if (rateConfig != null) {
            project.setBorrowRate(rateConfig.getBorrowInterestRate());
            project.setManageRate(rateConfig.getPlatformManageFeeRate());
            project.setGuaranteeRate(rateConfig.getGuaranteeServiceFeeRate());
            project.setRepaymentType(rateConfig.getRepaymentMode());
            project.setDeadline(rateConfig.getDeadline());
            project.setDeadlineUnit(new Integer(1).byteValue());
        }
        List<TradeConfigEnum> enums = new ArrayList<>();
        enums.add(TradeConfigEnum.CARDNIU_OVERDUEFEERATE);
        enums.add(TradeConfigEnum.CARDNIU_INVESTDEADLINE);
        Map<String, String> configMap = tradeConfigLocalService.selectTradeConfigByEmuns(enums);
        if (configMap != null) {
            project.setPenaltyRate(new BigDecimal(configMap.get(TradeConfigEnum.CARDNIU_OVERDUEFEERATE.getConfigKey())));
        }
        CardniuUserDeviceInfoDTO cardniuUserDeviceInfoDTO = loanDubboService.userDeviceInfo(fastLoanProjectDTO.getUserId());
        project.setLongitude(cardniuUserDeviceInfoDTO.getLongitude());
        project.setLatitude(cardniuUserDeviceInfoDTO.getLatitude());
        project.setLoanProvince(cardniuUserDeviceInfoDTO.getProvince());
        project.setLoanCity(cardniuUserDeviceInfoDTO.getCity());
        project.setLoanArea(cardniuUserDeviceInfoDTO.getCountry());
        project.setOrgId(OrgConstant.NIIWOO_ORG_ID);
        project.setIsMasterBorrower((byte) 1);

        // 初次申请金额、期限
        project.setFirstDeadline(project.getDeadline());
        project.setFirstDeadlineUnit(project.getDeadlineUnit());
        project.setFirstAmount(project.getContractAmount());

        projectMapperExt.insertSelective(project);

        SpeedLoanApplyRequestDTO speedLoanApplyRequestDTO = new SpeedLoanApplyRequestDTO();
        SpeedLoanApplyRequestDTO.DeviceInfoRequestDTO deviceInfoRequestDTO = new SpeedLoanApplyRequestDTO.DeviceInfoRequestDTO();
        deviceInfoRequestDTO.setOs(cardniuUserDeviceInfoDTO.getOs());
        deviceInfoRequestDTO.setIp(cardniuUserDeviceInfoDTO.getIp());
        deviceInfoRequestDTO.setBrandType(cardniuUserDeviceInfoDTO.getBrand());

        String os = cardniuUserDeviceInfoDTO.getOs();
        if (os != null && !"".equals(os)) {
            if ("ios".equals(os)) {
                deviceInfoRequestDTO.setDeviceId(cardniuUserDeviceInfoDTO.getIdfa());
            } else if ("android".equals(os)) {
                deviceInfoRequestDTO.setDeviceId(cardniuUserDeviceInfoDTO.getImei());
            }
        }
        deviceInfoRequestDTO.setGps(cardniuUserDeviceInfoDTO.getProvince() + cardniuUserDeviceInfoDTO.getCity() + cardniuUserDeviceInfoDTO.getCountry() + cardniuUserDeviceInfoDTO.getStreet() + cardniuUserDeviceInfoDTO.getStreetNumber());
        if (!StringUtils.isEmpty(cardniuUserDeviceInfoDTO.getJailBreak()) && cardniuUserDeviceInfoDTO.getJailBreak().equals("否")) {

            deviceInfoRequestDTO.setDeviceStatus(new Integer(0).byteValue());
        } else if (!StringUtils.isEmpty(cardniuUserDeviceInfoDTO.getJailBreak()) && cardniuUserDeviceInfoDTO.getJailBreak().equals("是")) {
            deviceInfoRequestDTO.setDeviceStatus(new Integer(1).byteValue());
        }
        speedLoanApplyRequestDTO.setUserId(fastLoanProjectDTO.getUserId());
        speedLoanApplyRequestDTO.setDeviceInfo(deviceInfoRequestDTO);
        //插入设备信息
        speedLoanService.projectLoanDeviceInfoCreat(speedLoanApplyRequestDTO, project);
        //插入标的费率数据
        speedLoanService.cardniuProjectRateCreat(fastLoanProjectDTO.getUserId(), project.getProjectId(), fastLoanProjectDTO.getDeadLine());

        // 记录标的动态信息
        ProjectDynamicDTO projectDynamicDTO = new ProjectDynamicDTO();
        projectDynamicDTO.setUserId(project.getBorrowUserId());
        projectDynamicDTO.setProjectId(project.getProjectId());
        projectDynamicDTO.setTradeTypeEnum(ProjectDynamicDTO.TradeTypeEnum.REPAYMENT);
        projectDynamicDTO.setProjectDynamicEnum(ProjectDynamicEnum.APPLY_SUBMIT_SUCCESS);
        addProjectDynamicRecord(projectDynamicDTO);

        return project.getProjectId();
    }

    public Byte selectTeProjectFlag(String userId, Long projectId){
        return projectMapperExt.selectTeProjectFlag(userId,projectId);
    }

    public Integer queryOutstandingProjectWithoutOrg(String userId){
        return projectMapperExt.selectOutstandingProjectWithoutOrg(userId);
    }

    /**
     * 设置标的担保公司、逾期垫付天数
     * @param project
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void setProjectGuaranteeCompany(Project project) {
        AdvanceConfig advanceConfig = advanceConfigMapperExt.selectByPrimaryKey(project.getOrgId());
        if (advanceConfig == null) {
            log.info("未找到标的对应垫付配置，project={}", JSON.toJSONString(project));
            throw new BizException("TRD60005");
        }

        if (StringUtils.isEmpty(advanceConfig.getUserId()) || advanceConfig.getOverdueDays() == null || advanceConfig.getOverdueDays() < 0) {
            log.error("标的对应垫付配置错误，project={}, advanceConfig={}", JSON.toJSONString(project), JSON.toJSONString(advanceConfig));
            throw new BizException("TRD60006");
        }

        Project updateProject = new Project();

        updateProject.setProjectId(project.getProjectId());
        updateProject.setGuaranteeCompanyOrgId(advanceConfig.getUserId());
        updateProject.setAdvanceOverdueDays(advanceConfig.getOverdueDays());

        projectMapperExt.updateByPrimaryKeySelective(updateProject);
    }

    /**
     * 标的待发布
     * @param projectWaitPublishReqDTO
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void projectWaitPublish(ProjectWaitPublishReqDTO projectWaitPublishReqDTO){
        Project project=projectMapperExt.selectByPrimaryKey(projectWaitPublishReqDTO.getProjectId());
        if(project==null){
            log.error("标的待发布通知,标的ID={},标的不存在.", projectWaitPublishReqDTO == null ? null : projectWaitPublishReqDTO.getProjectId());
            throw new BizException("TRD20001");
        }

        ProjectWaitPublish projectWaitPublish2Select=projectWaitPublishMapperExt.selectByPrimaryKey(projectWaitPublishReqDTO.getProjectId());
        if(projectWaitPublish2Select!=null){
            log.error("标的待发布通知,标的ID={},标的已处于待发布中,请勿重复提交.", projectWaitPublishReqDTO == null ? null : projectWaitPublishReqDTO.getProjectId());
            throw new BizException("TRD_800017");
        }

        //将标的推入待发布列表;
        ProjectWaitPublish projectWaitPublish=new ProjectWaitPublish();
        projectWaitPublish.setProjectId(projectWaitPublishReqDTO.getProjectId());
        projectWaitPublish.setAuditTime(projectWaitPublishReqDTO.getAuditTime());
        projectWaitPublish.setAuditManageerId(projectWaitPublishReqDTO.getAuditManagerId());
        projectWaitPublish.setAuditManagerName(projectWaitPublishReqDTO.getAuditManagerName());
        projectWaitPublish.setPushFrom(projectWaitPublishReqDTO.getPushFromEnum().getType());
        projectWaitPublish.setLoanType(project.getLoanType());
        projectWaitPublish.setCreateTime(new Date());

        projectWaitPublishMapperExt.insertSelective(projectWaitPublish);
    }

    /**
     * 标的待发布转发布
     *
     * @param projectId 已满标标的ID
     */
    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void projectWaitPublishToPublish(Long projectId) {
        //待发布标重复处理校验
        Boolean projectIsInHand = redisTemplate.opsForValue().setIfAbsent(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + projectId, System.currentTimeMillis() + "");
        if (projectIsInHand) {
            redisTemplate.expire(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + projectId, 5, TimeUnit.MINUTES);
        } else {
            log.error("标的待发布转发布通知,满标标的ID={},当前满标标的的替补标的正在进行发布操作,请勿重复操作.", projectId);
            return;
        }


        //查询标的贷款类型
        Project project = projectMapperExt.selectByPrimaryKey(projectId);

        List<Byte> loanTypeList = new ArrayList<Byte>();
        boolean isNeedNewPublish = false;
//        boolean isCreditProject = isCreditProject(project.getLoanType());
//        boolean isMortgageProject = isMortgageProject(project.getLoanType());

        //信用标
//        if (isCreditProject) {
            loanTypeList.add(LoanTypeEnum.SOCIAL_SECURITY_LOAN.getValue());
            loanTypeList.add(LoanTypeEnum.CREDIT_CARD_LOAN.getValue());
            loanTypeList.add(LoanTypeEnum.SUPPLY_CHAIN_LOAN.getValue());
            loanTypeList.add(LoanTypeEnum.SPEED_LOAN.getValue());
            loanTypeList.add(LoanTypeEnum.OTHER_LOAN.getValue());
            loanTypeList.add(LoanTypeEnum.CONSUME_LOAN.getValue());
//        }
        //抵押标
//        else if (isMortgageProject) {
            loanTypeList.add(LoanTypeEnum.HOUSE_MORTGAGE_LOAN.getValue());
            loanTypeList.add(LoanTypeEnum.VEHICLE_MORTGAGE_LOAN.getValue());
//        }
        //其他
//        else {
//            log.error("标的待发布转发布通知,满标标的ID={},贷款类型不支持.", projectId);
//            return;
//        }

        String loanTypeListStr = org.apache.commons.lang3.StringUtils.join(loanTypeList, ",");
        try {
            Boolean notExists = redisTemplate.opsForValue().setIfAbsent(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr, System.currentTimeMillis() + "");
            if (notExists) {
                redisTemplate.expire(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr, 5, TimeUnit.MINUTES);
            } else {
                log.error("标的待发布转发布通知,满标标的ID={},当前满标标的的替补标的正在进行发布操作,请勿重复操作.", projectId);
                return;
            }

            //检查可投标的数量
            int investableProjectCount = projectInvestingMapperExt.selectInvestableProjectCount(loanTypeList);
//            if (isCreditProject) {
                String retailProjectShowSizeStr = tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.RETAIL_PROJECT_SHOW_SIZE);
                if (StringUtils.isEmpty(retailProjectShowSizeStr)) {
                    log.error("标的待发布转发布通知,满标标的ID={},散标投资列表数量未配置.", projectId);
                    return;
                }

                Integer retailProjectShowSize = Integer.parseInt(retailProjectShowSizeStr);
                if (investableProjectCount < retailProjectShowSize) {
                    isNeedNewPublish = true;
                }
//            } else if (isMortgageProject) {
//                String mortgageProjectShowSizeStr = tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.MORTGAGE_PROJECT_SHOW_SIZE);
//                if (StringUtils.isEmpty(mortgageProjectShowSizeStr)) {
//                    log.error("标的待发布转发布通知,满标标的ID={},抵押标投资列表数量未配置.", projectId);
//                    return;
//                }
//
//                Integer mortgageProjectShowSize = Integer.parseInt(mortgageProjectShowSizeStr);
//                if (investableProjectCount < mortgageProjectShowSize) {
//                    isNeedNewPublish = true;
//                }
//            }

            //可投标的若小于配置的投资列表数量,则需要新发布标的;
            if (isNeedNewPublish) {
                publishProject(loanTypeList);
            }
        } finally {
            //处理完成,释放标的锁与贷款类型锁;
            redisTemplate.delete(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + projectId);
            redisTemplate.delete(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr);
        }
    }

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void publishProject(List<Byte> loanTypeList) {
        ProjectWaitPublish projectWaitPublish = projectWaitPublishMapperExt.selectOneToPublish(loanTypeList);
        if (projectWaitPublish == null) {
            log.info("标的待发布转发布通知,标的类型={},当前无待发布标的.", org.apache.commons.lang3.StringUtils.join(loanTypeList,","));
            return;
        }

        Project project=projectMapperExt.selectByPrimaryKeyForUpdate(projectWaitPublish.getProjectId());
        if(project==null){
            log.info("标的待发布转发布通知,正在发布的标的ID={},标的不存在", projectWaitPublish.getProjectId());
            throw new BizException("TRD20001");
        }
        log.info("标的待发布转发布通知,正在发布的标的ID={},标的状态={}", project.getProjectId(), project.getStatus());
        if(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())){
            PushProjectRequestDTO pushProjectRequestDTO = new PushProjectRequestDTO();
            pushProjectRequestDTO.setProjectId(projectWaitPublish.getProjectId());
            pushProjectRequestDTO.setAuditedTime(projectWaitPublish.getAuditTime());
            pushProjectRequestDTO.setAuditManagerId(projectWaitPublish.getAuditManageerId());
            pushProjectRequestDTO.setAuditManagerName(projectWaitPublish.getAuditManagerName());
            pushProjectRequestDTO.setPushFromEnum(PushProjectRequestDTO.PushFromEnum.getEnumByType(projectWaitPublish.getPushFrom()));

            //上标
            log.info("标的待发布转发布通知,正在发布的标的ID={},调用分端接口参数={}", project.getProjectId(), JSON.toJSONString(pushProjectRequestDTO));
            rabbitTemplate.convertAndSend(MqConstant.ALLOT_CHANNEL_EXCHANGE, MqConstant.ALLOT_CHANNEL_ROUTING, pushProjectRequestDTO);
        }

        //将已发布标的从待发布池中移出
        projectWaitPublishMapperExt.deleteByPrimaryKey(projectWaitPublish.getProjectId());
        log.info("标的待发布转发布通知,正在发布的标的ID={},已从待发布池移出", project.getProjectId(),projectWaitPublish.getProjectId());
    }

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void publishAppointProject(Long projectId) {
        ProjectWaitPublish projectWaitPublish = projectWaitPublishMapperExt.selectByPrimaryKey(projectId);
        if (projectWaitPublish == null) {
            log.info("标的待发布转发布通知,标的ID={},标的在待发布池中不存在.", projectId);
            return;
        }

        Project project=projectMapperExt.selectByPrimaryKeyForUpdate(projectWaitPublish.getProjectId());
        if(project==null){
            log.info("标的待发布转发布通知,标的ID={},标的不存在", projectWaitPublish.getProjectId());
            return;
        }
        log.info("标的待发布转发布通知,标的ID={},标的状态={}", project.getProjectId(), project.getStatus());
        if(project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())){
            PushProjectRequestDTO pushProjectRequestDTO = new PushProjectRequestDTO();
            pushProjectRequestDTO.setProjectId(projectWaitPublish.getProjectId());
            pushProjectRequestDTO.setAuditedTime(projectWaitPublish.getAuditTime());
            pushProjectRequestDTO.setAuditManagerId(projectWaitPublish.getAuditManageerId());
            pushProjectRequestDTO.setAuditManagerName(projectWaitPublish.getAuditManagerName());
            pushProjectRequestDTO.setPushFromEnum(PushProjectRequestDTO.PushFromEnum.getEnumByType(projectWaitPublish.getPushFrom()));

            //上标
            log.info("标的待发布转发布通知,正在发布的标的ID={},调用分端接口参数={}", project.getProjectId(), JSON.toJSONString(pushProjectRequestDTO));
            rabbitTemplate.convertAndSend(MqConstant.ALLOT_CHANNEL_EXCHANGE, MqConstant.ALLOT_CHANNEL_ROUTING, pushProjectRequestDTO);
        }

        //将已发布标的从待发布池中移出
        projectWaitPublishMapperExt.deleteByPrimaryKey(projectWaitPublish.getProjectId());
        log.info("标的待发布转发布通知,标的ID={},已从待发布池移出", project.getProjectId(),projectWaitPublish.getProjectId());
    }

    /**
     * 标的待发布转发布扫描
     */
    public void projectWaitPublishToPublishScan() {
        String batchNo = DateUtils.getCurrDatetime();
        log.info("标的待发布转发布扫描,批次号={}", batchNo);
        CompletableFuture.runAsync(() -> {
            //查询散标
            List<Byte> creditProjectLoanTypeList = new ArrayList<Byte>();
            creditProjectLoanTypeList.add(LoanTypeEnum.SOCIAL_SECURITY_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.CREDIT_CARD_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.SUPPLY_CHAIN_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.SPEED_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.OTHER_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.CONSUME_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.HOUSE_MORTGAGE_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.VEHICLE_MORTGAGE_LOAN.getValue());
            creditProjectLoanTypeList.add(LoanTypeEnum.BUSINESS_LOAN.getValue());

            //检查可投标的数量
            int investableProjectCount;
            int retailProjectShowSize;

            String loanTypeListStr = org.apache.commons.lang3.StringUtils.join(creditProjectLoanTypeList, ",");
            try {
                Boolean notExists = redisTemplate.opsForValue().setIfAbsent(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr, System.currentTimeMillis() + "");
                if (notExists) {
                    redisTemplate.expire(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr, 5, TimeUnit.MINUTES);
                } else {
                    log.info("标的待发布转发布扫描,批次号={},当前散标正在进行发布操作", batchNo);
                    return;
                }
                investableProjectCount = projectInvestingMapperExt.selectInvestableProjectCount(creditProjectLoanTypeList);
                log.info("标的待发布转发布扫描,批次号={},本次扫描到的散标可投标数量={}", batchNo, investableProjectCount);
                String retailProjectShowSizeStr = tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.RETAIL_PROJECT_SHOW_SIZE);
                log.info("标的待发布转发布扫描,批次号={},当前配置散标可投标数量={},", batchNo, retailProjectShowSizeStr);
                if (StringUtils.isEmpty(retailProjectShowSizeStr)) {
                    log.info("标的待发布转发布扫描,批次号={},散标投资列表数量未配置", batchNo);
                    return;
                }

                retailProjectShowSize = Integer.parseInt(retailProjectShowSizeStr);

                List<ProjectWaitPublish> waitPublishProjectList = projectWaitPublishMapperExt.selectByLoanTypeList(creditProjectLoanTypeList);
                int differCount = retailProjectShowSize - investableProjectCount;

                int toNewPublishProjectCount = differCount <= 0 ? 0 : (waitPublishProjectList.size() == 0 ? 0 : (differCount > waitPublishProjectList.size() ? waitPublishProjectList.size() : differCount));
                log.info("标的待发布转发布扫描,批次号={},当前散标准备新发布标的数量={}", batchNo, toNewPublishProjectCount);
                for (int i = 0; i < toNewPublishProjectCount; i++) {
                    Long toPublishProjectId = waitPublishProjectList.get(i).getProjectId();
                    log.info("标的待发布转发布扫描,批次号={},当前散标正发布第{}个标,标的ID={}", batchNo, i + 1, toPublishProjectId);
                    publishAppointProject(toPublishProjectId);
                }
            }finally {
                redisTemplate.delete(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr);
            }
            log.info("标的待发布转发布扫描,批次号={},当前散标发布操作完成,已删除贷款类型锁.",batchNo);
        }).whenComplete((success, throwable) -> {
            if (throwable != null) {
                log.error("标的待发布转发布扫描,批次号={},本次散标扫描发生异常,原因={}",batchNo,throwable);
            } else {
                log.info("标的待发布转发布扫描,批次号={},本次散标扫描已完成.",batchNo);
            }
        });

//        CompletableFuture.runAsync(() -> {
//            //抵押标
//            List<Byte> mortgageProjectLoanTypeList = new ArrayList<Byte>();
//            mortgageProjectLoanTypeList.add(LoanTypeEnum.HOUSE_MORTGAGE_LOAN.getValue());
//            mortgageProjectLoanTypeList.add(LoanTypeEnum.VEHICLE_MORTGAGE_LOAN.getValue());
//
//            String loanTypeListStr=org.apache.commons.lang3.StringUtils.join(mortgageProjectLoanTypeList,",");
//
//            try {
//
//                Boolean notExists = redisTemplate.opsForValue().setIfAbsent(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr, System.currentTimeMillis() + "");
//                if (notExists) {
//                    redisTemplate.expire(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr, 5, TimeUnit.MINUTES);
//                } else {
//                    log.info("标的待发布转发布扫描,批次号={},当前抵押标正在进行发布操作", batchNo);
//                    return;
//                }
//
//                int mortgageProjectCount = projectInvestingMapperExt.selectInvestableProjectCount(mortgageProjectLoanTypeList);
//                String mortgageProjectCountSizeStr = tradeConfigLocalService.selectTradeConfigByEmun(TradeConfigEnum.MORTGAGE_PROJECT_SHOW_SIZE);
//                if (StringUtils.isEmpty(mortgageProjectCountSizeStr)) {
//                    log.info("标的待发布转发布扫描,批次号={},抵押标投资列表数量未配置", batchNo);
//                    return;
//                }
//
//                Integer mortgageProjectShowSize = Integer.parseInt(mortgageProjectCountSizeStr);
//                List<ProjectWaitPublish> waitPublishProjectList = projectWaitPublishMapperExt.selectByLoanTypeList(mortgageProjectLoanTypeList);
//                int differCount = mortgageProjectShowSize - mortgageProjectCount;
//
//                int toNewPublishProjectCount = differCount <= 0 ? 0 : (waitPublishProjectList.size() == 0 ? 0 : (differCount > waitPublishProjectList.size() ? waitPublishProjectList.size() : differCount));
//                log.info("标的待发布转发布扫描,批次号={},当前抵押标准备新发布标的数量={}", batchNo, toNewPublishProjectCount);
//                for (int i = 0; i < toNewPublishProjectCount; i++) {
//                    Long toPublishProjectId = waitPublishProjectList.get(i).getProjectId();
//                    log.info("标的待发布转发布扫描,批次号={},当前抵押标正发布第{}个标,标的ID={}", batchNo, i + 1, toPublishProjectId);
//                    publishAppointProject(toPublishProjectId);
//                }
//
//            }finally {
//                redisTemplate.delete(TradeRedisKey.PROJECT_WAIT_PUBLISH_TO_PUBLISH + loanTypeListStr);
//            }
//            log.info("标的待发布转发布扫描,批次号={},当前抵押标发布操作完成,已删除贷款类型锁.",batchNo);
//        }).whenComplete((success, throwable) -> {
//            if (throwable != null) {
//                log.error("标的待发布转发布扫描,批次号={},本次抵押标扫描发生异常,原因={}",batchNo,throwable);
//            } else {
//                log.info("标的待发布转发布扫描,批次号={},本次抵押标扫描已完成.",batchNo);
//            }
//        });
    }

    /**
     * 是信用标?
     * @param loanType
     */
    private boolean isCreditProject(Byte loanType){
        return loanType.equals(LoanTypeEnum.SOCIAL_SECURITY_LOAN.getValue()) ||
               loanType.equals(LoanTypeEnum.CREDIT_CARD_LOAN.getValue()) ||
               loanType.equals(LoanTypeEnum.SUPPLY_CHAIN_LOAN.getValue()) ||
               loanType.equals(LoanTypeEnum.SPEED_LOAN.getValue()) ||
               loanType.equals(LoanTypeEnum.OTHER_LOAN.getValue()) ||
                loanType.equals(LoanTypeEnum.CONSUME_LOAN.getValue());

    }

    /**
     * 是抵押标?
     * @param loanType
     */
    private boolean isMortgageProject(Byte loanType) {
        return loanType.equals(LoanTypeEnum.HOUSE_MORTGAGE_LOAN.getValue()) ||
                loanType.equals(LoanTypeEnum.VEHICLE_MORTGAGE_LOAN.getValue());
    }

    @Autowired
    private ProjectPackageLocalService projectPackageLocalService;


    @Transactional(isolation = Isolation.READ_COMMITTED)
    public void resetProjectChannelCode(Long projectId, String channelCode) {
        if (projectId == null || StringUtils.isEmpty(channelCode)) {
            log.error("标的重新分端出错：参数错误；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD10005");
        }

        Project project = projectMapperExt.selectByPrimaryKeyForUpdate(projectId);
        if (project == null) {
            log.error("标的重新分端出错：标的不存在；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD20001");
        }

        String oldChannelCode = project.getChannelCode();

        if (!ProjectStatusEnum.COLLECTING.getStatus().equals(project.getStatus())) {
            log.error("标的重新分端出错：标的状态错误；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD60003");
        }

        ProjectDeliveryChannel projectDeliveryChannel = projectDeliveryChannelMapperExt.selectByChannelCode(channelCode);
        if (projectDeliveryChannel == null) {
            log.error("标的重新分端出错：分端渠道配置不存在；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD3000002");
        }

        if (!Byte.valueOf("1").equals(projectDeliveryChannel.getStatus())) {
            log.error("标的重新分端出错：渠道未启用；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD3000003");
        }

        if (channelCode.equals(project.getChannelCode())) {
            log.info("标的重新分端：标的已分配到该端，不需要重新分配；projectId={}，channelCode={}", projectId, channelCode);
            return;
        }

        BigDecimal investedAmount = projectInvestOrderMapperExt.selectInvestedAmount(projectId);
        if (investedAmount != null) {
            log.error("标的重新分端出错：用户已投资；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD3000001");
        }

        BigDecimal investingAmount = projectInvestOrderMapperExt.selectInvestingAmount(projectId);
        if (investingAmount != null) {
            log.error("标的重新分端出错：有用户正在投资；projectId={}，channelCode={}", projectId, channelCode);
            throw new BizException("TRD3000001");
        }

        // 更新标的渠道编码，投资截止时间
        Project updateProject = new Project();
        updateProject.setProjectId(projectId);
        updateProject.setChannelCode(channelCode);
        updateProject.setInvestEndTime(getProjectInvestEndTime(project,projectDeliveryChannel));
        updateProject.setDeliverytime(new Date());
        updateProject.setUpdateTime(new Date());
        /**
         * v5.4.5之前分端发标的逻辑  从app-非app端，不加息，非-app到app，要加息  以前的是临时方案
         * v5.5.0跟产品确认t_tc_invest_add_interest_rate_config所有加息或者不加息从配置表取值，
         * 添加了channel——code字段，所有加息，需要通过 期限+利率+orgID+channel_code 一起查，配置表有加息，则加息，不一定只有app
         */
        this.projectRaiseRateHandle(project, updateProject);

        /*// 更新后渠道编码为APP的情况需要重置标的加息配置
        if (ChannelCodeEnum.APP.getChanelCode().equals(channelCode)) {
        	this.projectRaiseRateHandle(project, updateProject);
        }
        // 标的从APP渠道更换到其它渠道，如果有投资加息需要重置
        else if (ChannelCodeEnum.APP.getChanelCode().equals(project.getChannelCode()) &&
        		(project.getActivityId() != null || project.getActivityRaiseRate() != null)) {
        	projectMapperExt.resetProjectRaiseRate(projectId);
        }*/
        if(updateProject.getActivityId() == null || updateProject.getActivityRaiseRate() == null){
            projectMapperExt.resetProjectRaiseRate(projectId);
        }

        projectMapperExt.updateByPrimaryKeySelective(updateProject);

        // 更新投资中标的渠道编码
        ProjectInvesting updateProjectInvesting = new ProjectInvesting();
        updateProjectInvesting.setProjectId(projectId);
        updateProjectInvesting.setChannelCode(channelCode);
        updateProjectInvesting.setInvestEndTime(updateProject.getInvestEndTime());
        //if (ChannelCodeEnum.APP.getChanelCode().equals(channelCode)) {
        updateProjectInvesting.setActivityId(updateProject.getActivityId());
        updateProjectInvesting.setActivityRaiseRate(updateProject.getActivityRaiseRate());
        //}
        // 标的从APP渠道更换到其它渠道，如果有投资加息需要重置
        /*else if (ChannelCodeEnum.APP.getChanelCode().equals(project.getChannelCode()) &&
        		(project.getActivityId() != null || project.getActivityRaiseRate() != null)) {
        	projectInvestingMapperExt.resetProjectRaiseRate(projectId);
        }*/
        if(updateProject.getActivityId() == null || updateProject.getActivityRaiseRate() == null){
            projectInvestingMapperExt.resetProjectRaiseRate(projectId);
        }

        projectInvestingMapperExt.updateByPrimaryKeySelective(updateProjectInvesting);

        projectPackageLocalService.resetProjectChannelCode(projectId);

        // 若标的分到智能投，则需要发送MQ通知智能投业务处理;
        if (isZhiNengTou(channelCode)) {
            rabbitTemplate.convertAndSend(MqConstant.PROJECT_PACKAGE_ADD_PROJECT_EXCHANGE, MqConstant.PROJECT_PACKAGE_ADD_PROJECT_ROUTING, projectId);
        }

        log.info("标的重新分端成功，projectId={}，oldChannelCode={}, newChannelCode={}", projectId, oldChannelCode, channelCode);
    }

    /**
     * 是否智能投或新手智能投标的
     * @param channelCode
     * @return
     */
    public boolean isZhiNengTou(String channelCode) {
        return StringUtils.isEmpty(channelCode) ? false : (channelCode.equalsIgnoreCase(com.niiwoo.civet.base.enums.ChannelCodeEnum.ZHINENGTOU.getChanelCode()) || channelCode.equalsIgnoreCase(com.niiwoo.civet.base.enums.ChannelCodeEnum.XINSHOUZHINENGTOU.getChanelCode()));
    }

    private boolean isXinShouBiao(String channelCode){
        return StringUtils.isEmpty(channelCode) ? false : (channelCode.equalsIgnoreCase(com.niiwoo.civet.base.enums.ChannelCodeEnum.XINSHOUBIAO.getChanelCode()));
    }

    /**
     * 运营投资加息活动处理
     *
     * @param project
     * @param project2Update
     */
    private void projectRaiseRateHandle(Project project, Project project2Update) {
    	try {
	    	AddInterestRateConfigRequestDTO addInterestRateConfigRequestDTO = new AddInterestRateConfigRequestDTO();
	    	addInterestRateConfigRequestDTO.setOrgId(project.getOrgId());
	    	addInterestRateConfigRequestDTO.setDeadline(project.getDeadline());
	    	addInterestRateConfigRequestDTO.setDeadlineUnit(project.getDeadlineUnit());
	    	addInterestRateConfigRequestDTO.setRepaymentType(project.getRepaymentType());
	    	addInterestRateConfigRequestDTO.setChannelCode(project2Update.getChannelCode());
	    	log.info("###发布借款###查询是否有符合条件的投资加息活动，ProjectId:{}, 请求参数：{}", project.getProjectId(), JSONObject.toJSONString(addInterestRateConfigRequestDTO));
	    	AddInterestRateConfigResponseDTO addInterestRateConfigResponseDTO = projectInvestPrizeService.getAddInterestRateConfig(addInterestRateConfigRequestDTO);
	    	log.info("###发布借款###查询是否有符合条件的投资加息活动，ProjectId:{}, 返回结果：{}", project.getProjectId(), JSONObject.toJSONString(addInterestRateConfigResponseDTO));

	    	if (null == addInterestRateConfigResponseDTO || null == addInterestRateConfigResponseDTO.getId()) return;

	    	// 返回活动更新活动ID以及活动利率
	    	project2Update.setActivityId(addInterestRateConfigResponseDTO.getId());
	    	project2Update.setActivityRaiseRate(addInterestRateConfigResponseDTO.getAddInterestRate());
    	} catch (Exception e) {
    		log.error("推标,标的ID={},查询运营投资加息活动异常.", project.getProjectId(), e);
		}
    }

    /**
     * 分端
     * @param pushProjectRequestDTO
     */
    @Transactional(isolation = Isolation.READ_COMMITTED,rollbackFor = Exception.class)
    public void allotChannel(PushProjectRequestDTO pushProjectRequestDTO){
        try {
            //计算分端与筹款结束时间
            log.info("标的分端通知,标的ID={},请求参数={}", pushProjectRequestDTO == null ? null : pushProjectRequestDTO.getProjectId(), JSON.toJSONString(pushProjectRequestDTO));
            Project project = projectCommonService.queryProjectBasicInfo(pushProjectRequestDTO.getProjectId());
            String channelCode = speedLoanDispenseChannelService.getChannelCode(project);
            ProjectDeliveryChannel projectDeliveryChannel = projectDeliveryChannelMapperExt.selectByChannelCode(channelCode);
            Project project2Update = new Project();
            project2Update.setProjectId(pushProjectRequestDTO.getProjectId());
            project2Update.setChannelCode(channelCode);
            log.info("标的分端通知,标的ID={},当前分端信息={}", pushProjectRequestDTO == null ? null : pushProjectRequestDTO.getProjectId(), JSON.toJSONString(projectDeliveryChannel));
            if (!(isZhiNengTou(project2Update.getChannelCode()) || isXinShouBiao(project2Update.getChannelCode()))) {
                project2Update.setInvestEndTime(getProjectInvestEndTime(project, projectDeliveryChannel));
            }

            Date now = new Date();
            project2Update.setDeliverytime(now);
            project2Update.setUpdateTime(now);

            projectCommonService.updateByPrimaryKeySelective(project2Update);

            //发送上标MQ
            log.info("标的分端通知,标的ID={},标的状态={}", project.getProjectId(), project.getStatus());
            if (project.getStatus().equals(ProjectStatusEnum.CHECKING.getStatus())) {
                //上标
                log.info("标的分端通知,标的ID={},调用上标接口参数={}", project.getProjectId(), JSON.toJSONString(pushProjectRequestDTO));
                rabbitTemplate.convertAndSend(MqConstant.PROJECT_PUBLISH_EXCHANGE, MqConstant.PROJECT_PUBLISH_ROUTING, pushProjectRequestDTO);
            }
        }catch(Exception e){
            log.error("标的分端通知,标的ID={},allotChannel方法发生系统异常,原因={}", pushProjectRequestDTO == null ? null : pushProjectRequestDTO.getProjectId(), e);
            throw e;
        }
    }

    /**
     * 计算标的筹款结束时间
     * @param
     * @return
     */
    private Date getProjectInvestEndTime(Project project,ProjectDeliveryChannel projectDeliveryChannel){
        //判断分端开关状态(0:关闭;1:开启)
        String projectDeliveryChannelStatus = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.PROJECTDELIVERYCHANNELSTATUS);
        Date now=new Date();
        Date investEndTime=null;
        //若分端开关已开启
        if ((!StringUtils.isEmpty(projectDeliveryChannelStatus)) && "1".equals(projectDeliveryChannelStatus)) {
            //筹标时长单位：小时
            if(projectDeliveryChannel.getDurationUnit().equals((byte)1)){
                log.info("标的分端通知,标的ID={},该渠道筹款时长单位：小时", project.getProjectId());
                investEndTime=org.apache.commons.lang3.time.DateUtils.addHours(now,projectDeliveryChannel.getDuration());
            }
            //筹标时长单位：天
            else{
                log.info("标的分端通知,标的ID={},该渠道筹款时长单位：天", project.getProjectId());
                investEndTime=org.apache.commons.lang3.time.DateUtils.addDays(now,projectDeliveryChannel.getDuration());
            }
        }
        //若分端开关已关闭,则直接取APP筹款时长配置
        else{
            log.info("标的分端通知,标的ID={},分端开关未开启,应用APP端默认筹款时长配置.", project.getProjectId(),investEndTime);
            String investEndConfigDay = tradeConfigService.selectTradeConfigByEmun(TradeConfigEnum.INVESTDEADLINE);
            investEndTime = org.apache.commons.lang3.time.DateUtils.addDays(now,Integer.valueOf(investEndConfigDay));

            // 机构标的筹款时长
            if (ProjectTypeEnum.ORG_SPEEDLOAN_NEW.getValue().equals(project.getType())) {
                try {
                    OrganizationBaseConfigRespDTO config = organizationDubboService.selectOrganizationBaseConfigByOrgId(project.getOrgId());
                    if (config != null && config.getFundraisingDeadLineUnit() != null && config.getFundraisingDeadLine() != null) {
                        // 筹款期限类型：0:天、1:小时
                        if (Byte.valueOf("0").equals(config.getFundraisingDeadLineUnit())) {
                            investEndTime = org.apache.commons.lang3.time.DateUtils.addDays(now, config.getFundraisingDeadLine());
                        } else {
                            investEndTime = org.apache.commons.lang3.time.DateUtils.addHours(now, config.getFundraisingDeadLine());
                        }
                        log.info("标的分端通知, 机构标的ID={}, 最终计算得出筹款截止时间={}", project.getProjectId(), investEndTime);
                    }
                } catch (Exception e) {
                    log.error("计算机构标的筹款截止时间出错：标的ID={}，机构ID={}", project.getProjectId(), project.getOrgId(), e);
                }
            }
        }

        log.info("标的分端通知,标的ID={},最终计算得出筹款截止时间={}", project.getProjectId(),investEndTime);
        return investEndTime;
    }

    /**
     * 融360确认借款
     * @param projectId
     */
    @Transactional
    public void r360ConfirmLoan(Long projectId) {
        // 修改标的状态为“已确认”
        Project project = new Project();
        project.setProjectId(projectId);
        project.setSubStatus(ProjectSubStatusEnum.CHECKING_FOR_CONFIRMED.getSubStatus());
        project.setConfirmTime(new Date());

        projectMapperExt.updateByPrimaryKeySelective(project);

        // 标的进入待发布列表
        ProjectWaitPublishReqDTO projectWaitPublishReqDTO = new ProjectWaitPublishReqDTO();
        projectWaitPublishReqDTO.setProjectId(projectId);
        projectWaitPublishReqDTO.setPushFromEnum(ProjectWaitPublishReqDTO.PushFromEnum.AUTO_AUDIT);
        projectWaitPublishReqDTO.setAuditTime(new Date());

        rabbitTemplate.convertAndSend(MqConstant.PROJECT_WAIT_PUBLISH_EXCHANGE, MqConstant.PROJECT_WAIT_PUBLISH_ROUTING, projectWaitPublishReqDTO);

        // 通知资产端
        R360OrderStatusChangedDTO r360OrderStatusChangedDTO = new R360OrderStatusChangedDTO();
        r360OrderStatusChangedDTO.setProjectId(project.getProjectId());
        r360OrderStatusChangedDTO.setR360OrderStatusEnumCode(R360OrderStatusEnum.CONFIRM_LOAN.getCode());
        //r360OrderStatusChangedDTO.setR360OrderProjectSubStatusEnum(R360OrderProjectSubStatusEnum.CHECKING_FOR_CONFIRMED);
        r360OrderStatusChangedDTO.setUpdateDate(new Date());

        rabbitTemplate.convertAndSend(MqConstants.Exchange.ASSET_EXCHANGE, MqConstants.getRoutingKey(MqConstants.Exchange.ASSET_EXCHANGE, MqConstants.Queue.ORDER_STATUS_NOFIFY_FOR_COMMON), r360OrderStatusChangedDTO);

        log.info("融360确认借款成功，projectId={}", projectId);
    }

    /**
     * 更新机构标的状态
     * @param projectId
     * @param status
     * @param subStatus
     */
    @Transactional
    public void updateOrgProjectStatus(Long projectId, Byte status, Integer subStatus) {
        Project project = new Project();
        project.setProjectId(projectId);
        project.setStatus(status);
        project.setSubStatus(subStatus);

        projectMapperExt.updateByPrimaryKeySelective(project);

        AssetOrgProject orgProject = new AssetOrgProject();
        orgProject.setProjectId(projectId);
        orgProject.setProjectStatus(status);
        orgProject.setSubStatus(subStatus);

        assetOrgProjectMapperExt.updateByPrimaryKeySelective(orgProject);
    }

    public ProjectDetailDTO getProjectById(Long projectId) {
        log.info("getProjectById projectId={}", projectId);

        ProjectDetailDTO projectDetailDTO = new ProjectDetailDTO();

        // 查询标的
        Project project = projectMapperExt.selectByPrimaryKey(projectId);

        if (project == null) {
            log.info("getProjectById:{}查询无结果", projectId);
            return null;
        }

        projectDetailDTO.setDeadline(project.getDeadline());
        projectDetailDTO.setDeadlineUnit(project.getDeadlineUnit());
        projectDetailDTO.setProjectId(project.getProjectId());
        projectDetailDTO.setBorrowRate(project.getBorrowRate());
        projectDetailDTO.setActivityRaiseRate(project.getActivityRaiseRate());
        // 查询借款人头像，昵称
        UserAccountPropertyResponse userAccountPropertyResponse = userAccountDubboService.getAccountProperty(project.getBorrowUserId());
        log.info("=================userAccountDubboService.getAccountProperty: userType:{},nickName:{},headImg:{}",
                userAccountPropertyResponse.getUserType(),userAccountPropertyResponse.getNickName(),userAccountPropertyResponse.getHeadUrl());
        if (userAccountPropertyResponse != null) {
            projectDetailDTO.setBorrowUserId(userAccountPropertyResponse.getUserId());
            projectDetailDTO.setBorrowHeadImage(userAccountPropertyResponse.getHeadUrl());
            projectDetailDTO.setBorrowNickName(userAccountPropertyResponse.getNickName());
            projectDetailDTO.setUserType(userAccountPropertyResponse.getUserType().getValue());
        }
        return projectDetailDTO;
    }

    @Transactional
    public void resetInvestEndTime(Long projectId) {
        Project project = projectMapperExt.selectByPrimaryKey(projectId);
        log.info("resetInvestEndTime project:{}", JSON.toJSON(project));
        if(project.getInvestEndTime() == null) {
            ProjectDeliveryChannel projectDeliveryChannel = projectDeliveryChannelMapperExt.selectByChannelCode(project.getChannelCode());

            Project updateProjectRecord = new Project();
            updateProjectRecord.setProjectId(projectId);
            updateProjectRecord.setInvestEndTime(getProjectInvestEndTime(project,projectDeliveryChannel));
            projectMapperExt.updateByPrimaryKeySelective(updateProjectRecord);

            ProjectInvesting projectInvesting = new ProjectInvesting();
            projectInvesting.setProjectId(projectId);
            projectInvesting.setInvestEndTime(updateProjectRecord.getInvestEndTime());
            projectInvestingMapperExt.updateByPrimaryKeySelective(projectInvesting);
        }
    }

    public Integer queryMaxDueDays(String userId) {
		Integer maxDueDays = 0;
		//查询逾期的应还款日期列表
		List<Date> refundDateList = projectBorrowerSummaryMapperExt.queryRefundDateList(userId);
		if(refundDateList != null && !refundDateList.isEmpty()) {
			Integer temp = 0;
			for (Date date : refundDateList) {
				temp = Long.valueOf(DateUtils.daysBetween(date, new Date())).intValue();
				if(temp >= maxDueDays) {
					//获取最大逾期天数
					maxDueDays = temp;
				}
			}
		}
		return maxDueDays;
	}

    public List<String> queryFromSourceListByUserId(String userId) {
    	List<String> fromSourceResult = new ArrayList<>();
    	//查询标的来源
    	List<Byte> fromSourceList = projectMapperExt.queryFromSourceListByUserId(userId);
    	if(fromSourceList != null) {
    		for (Byte fs : fromSourceList) {
				ProjectFromSourceEnum pfse = ProjectFromSourceEnum.enumOf(fs);
				if(pfse != null) {
					fromSourceResult.add(pfse.getDesc());
				}
			}
    	}
    	return fromSourceResult;
    }

	public CustomSysBorrowerInfoDTO queryCurrentPeriodRefundInfoByProjectId(Long projectId) {
		return projectBorrowerPlanNewMapperExt.queryCurrentPeriodRefundInfoByProjectId(projectId);
	}

	public List<CustomSysBorrowerInfoDTO> queryRefundListInfoByProjectId(Long projectId) {
		return projectBorrowerPlanNewMapperExt.queryRefundListInfoByProjectId(projectId);
	}

    @Transactional(isolation = Isolation.READ_COMMITTED)
    public List<Project> selectNeedRecallAutoAuditModelRecord() {
        return projectMapperExt.selectNeedRecallAutoAuditModelRecord();
    }


    /**
     * 标的使用红包
     * @param projectId
     * @param prizeId
     */
    @Transactional
    public void setProjectPrizeId(Long projectId, Long prizeId) {
        Project project = new Project();

        project.setProjectId(projectId);
        project.setPrizeId(prizeId);

        projectMapperExt.updateByPrimaryKeySelective(project);

        log.info("标的使用红包，projectId={}，prizeId={}", projectId, prizeId);
    }
}
